Developer API

OAuth apps

Let creators connect your tool with a click: the browser flow with PKCE, the device flow and app tokens.

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.

  1. Create a random code_verifier of 43 to 128 characters, and its code_challenge: the SHA-256 hash, base64url-encoded without padding.
  2. Create a random state and remember it.
  3. 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
  1. The creator signs in if needed and sees the consent screen. Gleem then sends the browser back to your redirect_uri with code, your state and iss=https://gleem.gg. Check that state matches what you sent. If the creator cancels, or the request is invalid, you get error (for example access_denied) and your state instead.
  2. 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_uri must 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). Register http://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.1 or [::1], not localhost.
  • 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 an https URI. 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.

  1. 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
}
  1. Show the user_code and verification_uri, or a QR code of verification_uri_complete, and ask the creator to open it.
  2. Poll the token endpoint every interval seconds:
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.