Developer API
OAuth apps
OAuth lets a creator connect your tool by clicking Allow instead of pasting a token. Register your tool once as an app. The creator is sent to Gleem, sees your app's name and the permissions it asks for, and approves. Your tool then receives tokens limited to those permissions, and the creator can disconnect it on their dashboard at any time.
Everything else stays the same: every /api/v1 endpoint, the scopes, the event envelope, the WebSocket channel and the error format. An OAuth access token goes in the same header as a personal token:
Authorization: Bearer gleem_oat_7f3k2m9q_eyJhbGciOiJFZERTQSIs…
Personal access tokens stay supported for scripts and a creator's own tools. Use OAuth when you build a tool for other creators.
Register an app
Register apps on the Developer page of your dashboard, under Your apps. Registering an app means accepting the Developer Terms. You enter:
- the name, an icon and a homepage;
- a privacy policy URL, which is required because every app receives viewer data;
- the client type: public for desktop, mobile and browser apps, or confidential for apps that run on your server;
- one or more redirect URIs for the browser flow;
- whether the app uses the device flow;
- a support email, which creators see and where we write if one of your webhooks keeps failing.
You get a client ID such as gla_7f3k2m9q. It is public and safe to ship in a desktop app. A confidential app also gets a client secret, gleem_cs_…, shown once. Keep it on your server and never ship it in an app. You can rotate it on the same page. The old secret keeps working for 24 hours after a rotation.
A new app works right away for up to 10 creators. Gleem reviews apps by hand: the name must not imitate Gleem or another product, the privacy policy must exist and cover viewer data, and the scopes must fit what the app does. A reviewed app loses the "Not reviewed by Gleem" notice on the consent screen and the 10-creator cap.
Choosing a flow
| Your tool | Flow |
|---|---|
| Can open a browser: Streamer.bot extensions, desktop and web apps | Authorization code with PKCE |
| Has no browser or runs headless: bots, CLIs over SSH, stream decks | Device flow |
| A server calling app-wide endpoints, for no particular creator | Client credentials |
The implicit and password grants are not offered. Every endpoint is described in the discovery document at /.well-known/oauth-authorization-server (RFC 8414), so most OAuth libraries can configure themselves from the issuer https://gleem.gg alone.
Authorization code with PKCE
PKCE (S256) is required for every app, public or confidential.
- Create a random
code_verifierof 43 to 128 characters, and itscode_challenge: the SHA-256 hash, base64url-encoded without padding. - Create a random
stateand remember it. - Open the creator's browser at the authorize endpoint. Every parameter is required:
GET https://gleem.gg/oauth/authorize
?response_type=code
&client_id=gla_7f3k2m9q
&redirect_uri=http://127.0.0.1:53682/callback
&scope=events:read tips:read queue:write
&state=Zq1…
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
- The creator signs in if needed and sees the consent screen. Gleem then sends the browser back to your
redirect_uriwithcode, yourstateandiss=https://gleem.gg. Check thatstatematches what you sent. If the creator cancels, or the request is invalid, you geterror(for exampleaccess_denied) and yourstateinstead. - Swap the code for tokens, from your tool, not the browser:
POST https://gleem.gg/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=…&redirect_uri=http://127.0.0.1:53682/callback&client_id=gla_7f3k2m9q&code_verifier=…
{
"token_type": "Bearer",
"access_token": "gleem_oat_…",
"expires_in": 3600,
"refresh_token": "gleem_ort_…",
"scope": "events:read tips:read queue:write"
}
A confidential app also sends its client_secret, in the body or as HTTP Basic auth (client_secret_basic). A code works once and expires after 60 seconds. Swapping the same code twice ends the session the first swap started.
Redirect URIs
redirect_urimust match a registered URI exactly.- A desktop app should listen on a loopback address, such as
http://127.0.0.1:53682/callback(RFC 8252). Registerhttp://127.0.0.1/callback: loopback URIs match on any port, so your app can pick a free one at run time. The path must still match. - Use the IP literal
127.0.0.1or[::1], notlocalhost. - A public mobile app can register a private-use URI scheme instead (RFC 8252 § 7.1), such as
gg.gleem.irl:/oauth/callback. The scheme must be a reverse domain name you control, in lower case and with at least one dot, and the URI must not have a fragment. It matches exactly, like anhttpsURI. Open the authorize URL in the system browser or a Custom Tab, never in a web view, and register the scheme with the OS so the redirect reaches your app. Confidential apps cannot register one. - Every other redirect URI has to use
https.
If the client_id or redirect_uri is wrong, Gleem shows the error in the browser and does not send it anywhere.
Device flow
For tools that cannot open a browser or receive a redirect (RFC 8628). The creator approves on any other device, such as their phone. Tick Device flow when you register the app.
- Ask for a code:
POST https://gleem.gg/oauth/device_authorization
Content-Type: application/x-www-form-urlencoded
client_id=gla_7f3k2m9q&scope=events:read tips:read
{
"device_code": "gleem_odc_…",
"user_code": "WDJB-MJHT",
"verification_uri": "https://gleem.gg/activate",
"verification_uri_complete": "https://gleem.gg/activate?code=WDJB-MJHT",
"expires_in": 600,
"interval": 5
}
- Show the
user_codeandverification_uri, or a QR code ofverification_uri_complete, and ask the creator to open it. - Poll the token endpoint every
intervalseconds:
POST https://gleem.gg/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=…&client_id=gla_7f3k2m9q
| Answer | Meaning |
|---|---|
400 authorization_pending |
The creator has not approved yet. Keep polling. |
400 slow_down |
You polled too fast. Add 5 seconds to your interval. |
400 access_denied |
The creator cancelled. Stop. |
400 expired_token |
The code expired after 10 minutes. Start again. |
200 with tokens |
Approved. Same token JSON as the browser flow. |
Codes work once. A confidential app also sends its client_secret on both requests. The consent screen always shows the code for the creator to compare, and warns them to enter only codes from a tool they are setting up themselves.
Client credentials
A confidential app can get an app token that acts for the app itself, not for any creator (RFC 6749 § 4.4). No creator approves it.
POST https://gleem.gg/oauth/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <base64 of client_id:client_secret>
grant_type=client_credentials
{
"token_type": "Bearer",
"access_token": "gleem_aat_…",
"expires_in": 3600
}
There is no refresh token: when it expires, ask again. An app token can call GET /api/v1/me, which then returns only {"app": {"id": "gla_…", "name": "…"}}, and the app-wide endpoints to come. Every creator endpoint refuses it with 403 forbidden. Public apps cannot use this grant.
Tokens
| Token | Prefix | Lifetime | Notes |
|---|---|---|---|
| OAuth access token | gleem_oat_ |
1 hour | Sent as Authorization: Bearer. |
| OAuth refresh token | gleem_ort_ |
6 months since its last use | Replaced on every refresh. |
| App access token | gleem_aat_ |
1 hour | Client credentials only. No refresh token. |
| Device code | gleem_odc_ |
10 minutes | Device flow only. Works once. |
| Client ID | gla_ |
Permanent | Public. |
| Client secret | gleem_cs_ |
Until rotated | Confidential apps only. Never in a desktop app. |
| Personal access token | gleem_pat_ |
Never, or 30 / 90 / 365 days | Unchanged. See Authentication. |
Treat tokens as opaque. Don't decode or validate them, and don't rely on their length or on any pattern beyond the prefix. Access tokens are around 350 to 450 characters today; store them in fields that hold at least 1,024.
Refreshing
POST https://gleem.gg/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=gleem_ort_…&client_id=gla_7f3k2m9q
Every refresh returns a new refresh token and invalidates the old one. Store the new one before you use the new access token. If an old refresh token is used again, Gleem assumes it was stolen and ends that install's session: the creator has to approve again on that device. Other installs keep working.
scope in every token response lists everything the creator has granted your app, which can be more than you asked for this time.
Installs and sessions
An app holds one grant per creator. Each approval, for example on a second PC, adds a session with its own refresh-token chain; earlier sessions keep working. If the new approval adds scopes, every session gets them on its next refresh. A grant holds up to 25 sessions, and the least recently used one is dropped when a new one would exceed that.
The creator sees your app once on their Developer page, with its sessions, and can end a single session or disconnect the app. When the last session ends, the grant ends too, together with any webhooks your app registered.
When a token stops working
A 401 for an OAuth access token says why in its header (RFC 6750):
WWW-Authenticate: Bearer error="invalid_token", error_description="expired"
expired: refresh and retry.revoked: the creator disconnected your app or ended this session, or the app was disabled. Ask the creator to connect again.
Unknown and malformed tokens get the plain 401 described in Errors.
Revoking
When a creator disconnects inside your tool, end the session on Gleem too (RFC 7009):
POST https://gleem.gg/oauth/revoke
Content-Type: application/x-www-form-urlencoded
token=gleem_ort_…&client_id=gla_7f3k2m9q
Either the access or the refresh token ends the session it belongs to. The answer is 200 for any token, known or not.
Scopes and consent
OAuth uses the same scopes as personal tokens (see Authentication), plus webhooks:write, which only apps can ask for. Request them space-separated in scope. The consent screen shows each one as a line in plain language, in the creator's language.
Consent is all or nothing: the creator allows every scope you ask for, or none. Ask only for what your tool uses.
- When your app already holds a grant and asks for more, the screen shows only the new scopes.
- A confidential app that asks again for scopes it already holds skips the screen and goes straight back to its redirect URI.
- A public app always sees the screen, because its client ID is public and another program could pose as it (RFC 8252 § 8.6).
- The device flow always shows the full screen.
Rate limits
OAuth tokens get the same 120 requests a minute as a personal token, counted per grant: one app on one creator account, across all its sessions. App tokens are counted per app. The token endpoints allow 60 requests a minute per app and address.
WebSocket
POST /api/v1/socket/auth accepts OAuth access tokens with events:read, or irl:read for IRL device telemetry (and irl:network on top for its network channel), exactly like personal tokens. A subscribed socket stays open after its access token expires. A revoked session or app is refused at the next channel authorisation.