Skip to main content

OAuth 2.1

For an application that acts on behalf of a user. They approve the exact permissions you ask for, you receive a token, and your app never sees their password — or more access than that user already has.

Standard authorization code + PKCE for public clients. No client secret is issued.

Base URLhttps://api.account.telebroad.com
Discovery/.well-known/oauth-authorization-server
PKCERequired, S256
Access token~1 hour
Refresh tokenRotates on every use

The flow​

  1. Add your app: your company's admin does this once.
  2. Discover the endpoints, once.
  3. Authorize: send the user to consent.
  4. Exchange the code for tokens.
  5. Refresh when the access token expires.

1. Add your app​

Your company's admin adds the app once. They open Add your app, then enter:

  • the app's name,
  • the exact https callback URL(s), and
  • who can use it: everyone, or chosen roles.

When they save, copy the client_id. There is no client secret and none is ever issued: this is a public client, and PKCE is what proves the request came from you.

Rules that catch people out:

  • https only, on a real domain. No http, no localhost, no IP address, no wildcard, no # fragment and no desktop-app scheme. See why a callback URL is rejected.
  • The match is exact, on the full URL. At authorize time your redirect_uri must be character-for-character one of the saved callback URLs. A trailing slash or an http/https swap is a callback URL that isn't one of the app's URLs.
  • One app per company. Two companies can use the same callback URL, each with its own client_id. If you serve several customers, each customer adds your app and gives you their client_id.
  • A new app starts with owners only. The admin chooses who else can use it, see Let your staff use an app.

If someone signs in before the app is added, they see This app isn't set up for your company.

2. Discover​

curl https://api.account.telebroad.com/.well-known/oauth-authorization-server

Returns the live authorization_endpoint, token_endpoint, registration_endpoint and scopes_supported.

:::note Read the endpoints, don't copy them The URLs below are what discovery returns today, and they are the one part of the platform not under /api/public/v1. Take them from the discovery document at runtime and a future move costs you nothing. :::

3. Authorize​

Generate a PKCE pair per request:

code_verifier = base64url(32 random bytes) # secret, keep server-side
code_challenge = base64url( SHA-256(code_verifier) ) # goes in the URL

Send the user's browser to the discovered authorization_endpoint:

{authorization_endpoint}
?response_type=code
&client_id=tbapp_…
&redirect_uri=https://app.acme.example/oauth/callback
&scope=webhooks:read%20webhooks:write
&state=RANDOM_PER_SESSION
&code_challenge=CHALLENGE
&code_challenge_method=S256

They return to redirect_uri?code=…&state=…, or ?error=access_denied if they declined. Verify state matches the value you sent.

4. Exchange the code​

Form-encoded, not JSON:

curl -X POST https://api.account.telebroad.com/api/v1/oauth/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d grant_type=authorization_code \
-d code=AUTHORIZATION_CODE \
-d code_verifier=CODE_VERIFIER \
-d client_id=tbapp_… \
-d redirect_uri=https://app.acme.example/oauth/callback
{
"access_token": "eyJ…",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "…",
"scope": "webhooks:read webhooks:write"
}

Check scope — it is what was actually granted, which may be less than you asked for.

5. Refresh​

curl -X POST https://api.account.telebroad.com/api/v1/oauth/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d grant_type=refresh_token \
-d refresh_token=REFRESH_TOKEN \
-d client_id=tbapp_…

Returns a new access token and a new refresh token. Refresh tokens rotate — persist the newest one every time. Reusing an old one fails.

AI assistants and tools that register themselves​

Assistants such as Claude, ChatGPT and the Telebroad Playground cannot ask an admin to add them first, so they register themselves with dynamic client registration. This is only for them. If you are building an app for a company, use Add your app.

curl -X POST https://api.account.telebroad.com/api/v1/oauth/register \
-H 'Content-Type: application/json' \
-d '{
"client_name": "Example Assistant",
"redirect_uris": ["https://assistant.example/oauth/callback"]
}'
{
"client_id": "tbmcp_…",
"redirect_uris": ["https://assistant.example/oauth/callback"]
}

Their redirect_uris must be HTTPS on a host Telebroad has approved, with one standing exception: loopback callbacks work with no approval and no HTTPS. A registration that is refused returns this error.

Loopback callbacks​

http://localhost:3000/callback
http://127.0.0.1:53682/callback
http://[::1]:8080/callback

localhost, 127.0.0.1 and [::1] are accepted on any port, over http, with no approval. This is how desktop and command-line assistants connect. It does not apply to your company's apps, which take saved https callbacks only.

This is RFC 8252 §7.3, loopback interface redirection. Any port is accepted because a native app binds whatever the OS gives it and cannot register the number in advance.

:::note Why this is safe even though anyone can register a client The authorization code is delivered to the user's own machine. An attacker who registered a client pointing at 127.0.0.1 would be sending the code to the victim's loopback interface, which they cannot reach — to read it they would already need code running on that machine, and at that point the browser session is theirs anyway.

That is not true of an internet host, which is exactly why those are approved. :::

The exception matches the host, not a substring of it: https://localhost.evil.example/cb is an ordinary internet host and still needs approval.

Call the API​

curl https://api.account.telebroad.com/api/public/v1/webhooks \
-H "Authorization: Bearer ACCESS_TOKEN"

Errors​

StatusMeaningAction
401Missing or expired tokenRefresh, then re-authorize if that fails
403 insufficient_scopeToken lacks the scope; the scope field names itRe-authorize with that scope
403 permission_deniedScope is fine — the user's role lacks the permissionTheir admin must change their role
400 on /tokenBad or expired code, PKCE mismatch, or wrong redirect_uriRestart the flow
400 invalid_grant on /tokenThe connection, the app or the user's role was removed, or the refresh token expiredNever retry. See Handling revoked access
Sign-in page errorThe app isn't set up, the user isn't allowed yet, or the callback URL doesn't matchSee OAuth errors

That third row is the one people lose time on. See Scopes.

Security requirements​

  • PKCE (S256) is mandatory. No client secret exists to fall back on.
  • Redirect URIs must be https and match one of your app's callback URLs exactly.
  • Rotate refresh tokens — store the newest after every refresh.
  • Least privilege — request the minimum scopes; a user who sees a smaller consent screen is a user who says yes.