OAuth 2.0 apps

Register an app, authorize users, exchange codes for tokens, and call Hypery Hub APIs.

Third-party apps use OAuth 2.0 with PKCE support. Users approve scopes in the hub; your app receives an authorization code on the redirect URI, then exchanges it for access_token and refresh_token. Use the access token as Authorization: Bearer on /api/v1/* and management routes. For a React app with prebuilt modals and token handling, see React client — auth & modals.

Endpoints

StepMethodURL
Authorize (browser redirect)GEThttps://hypery.ai/api/oauth/authorize
Token exchange / refreshPOSThttps://hypery.ai/api/oauth/token
Device sign-in for CLIs and TVs (Device flow)POSThttps://hypery.ai/api/oauth/device_authorization
Invite someone by email (Invitations)POSThttps://hypery.ai/api/oauth/invitations

(Replace the host with your hub URL in development, e.g. http://localhost:3001.)


Authorize — query parameters

ParameterRequiredDescription
client_idYesOAuth client id from the registered app
response_typeYesMust be code
redirect_uriYesMust match a URI registered on the app
scopeYesSpace-separated scopes (e.g. read write ai:chat billing:read). Scopes your app isn't registered for are dropped, not fatal; see Scopes
stateRecommendedOpaque value to prevent CSRF; returned on redirect
code_challengeFor PKCEBASE64URL(SHA256(code_verifier))
code_challenge_methodWith PKCES256 (the only supported method; plain is rejected)

Success redirects to redirect_uri?code=...&state=....

Native apps may register a loopback redirect such as http://127.0.0.1/callback or http://[::1]/callback and use any port at runtime (RFC 8252 §7.3); the scheme, IP literal and path must match. http://localhost is a DNS name, so it only matches exactly, port included.


Token — JSON body

Authorization code

FieldRequiredDescription
grant_typeYesauthorization_code
codeYesCode from the redirect
client_idYesSame as authorize
redirect_uriYesSame as authorize
client_secretFor confidential clientsApp secret
code_verifierFor PKCEOriginal verifier

Refresh token

FieldRequired
grant_typerefresh_token
refresh_tokenYes
client_idYes
client_secretIf confidential

Client id/secret may also be supplied via HTTP Basic on the token request, and the body may be application/x-www-form-urlencoded instead of JSON.

A confidential client that presents a valid client_secret gets a larger token-endpoint rate limit per client and IP (for example, a backend exchanging codes for many users from one address). Requests without a secret, or with a wrong one, share the stricter per-IP limit.

Device code (see Device flow)

FieldRequired
grant_typeurn:ietf:params:oauth:grant-type:device_code
device_codeYes
client_idYes
client_secretIf the device authorization request sent one

Example token response (success)

{
  "access_token": "…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "…",
  "scope": "read write ai:chat"
}

scope lists the scopes actually granted, which can be fewer than you requested when your app isn't registered for some of them (Scopes).


Code examples

// 1) Send the user to authorize (browser)
const u = new URL('https://hypery.ai/api/oauth/authorize');
u.searchParams.set('client_id', 'YOUR_CLIENT_ID');
u.searchParams.set('redirect_uri', 'https://yourapp.com/callback');
u.searchParams.set('response_type', 'code');
u.searchParams.set('scope', 'read write ai:chat billing:read');
u.searchParams.set('state', crypto.randomUUID());
window.location.href = u.toString();
 
// 2) On your server, exchange the code
const tokenRes = await fetch('https://hypery.ai/api/oauth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    grant_type: 'authorization_code',
    code: authorizationCodeFromQuery,
    client_id: 'YOUR_CLIENT_ID',
    client_secret: 'YOUR_CLIENT_SECRET',
    redirect_uri: 'https://yourapp.com/callback',
  }),
});
const tokens = await tokenRes.json();

Next steps