Before you start
- Contact Peliqan support and request activation of OAuth Apps in your Peliqan account
- Register an OAuth2 app in your Peliqan account: Settings → OAuth2 Apps, and select at least one scope
- Public app (no client secret) — browser/mobile apps. Use
authorization_code; it also gets a rotatingrefresh_token(no secret needed, PKCE plus rotation cover that). - Confidential app (has a client secret). Can additionally use
client_credentials(server-to-server, no user), and must also sendclient_secretwhen refreshing a token. - PKCE (
code_challenge/code_verifier, S256 only) is mandatory forauthorization_code, there is no way to opt out (RFC 9700). - Always pass an explicit
scope. An authorize request with noscopeand nodefault_scopesconfigured on the app is rejected withinvalid_scope.
Base URLs
All examples below use a
BASE_URL constant — set it to whichever of these applies to you.
Endpoint reference
1. Authorization code flow (user login)
This is “Login with Peliqan” — the user’s browser visits Peliqan, logs in, approves your app, and gets redirected back to you with acode. PKCE requires a code_verifier you generate and keep secret, and a code_challenge (its SHA-256 hash) you send up front.
- TypeScript
- Python
- Curl
Works in Node.js 18+, browsers, Bun, and Deno — uses only
fetch and the global Web Crypto API (crypto.subtle), no packages.2. Refreshing a token
Peliqan rotates refresh tokens on every use — the old one is revoked, so always persist the newrefresh_token from the response, not just the new access token. Both public and confidential apps get a refresh token; confidential apps must additionally authenticate with client_secret on every refresh — public apps don’t send one.
- TypeScript
- Python
- Curl
3. Client credentials (server-to-server, no user)
Requires a confidential app (has a client secret) — this grant is not available to public apps. Fully scriptable, no browser needed.- TypeScript
- Python
- Curl
4. Other endpoints
Revoke, introspect, get the logged-in user’s identity, list available scopes, and fetch the RFC 8414 discovery document.- TypeScript
- Python
- Curl
Error handling
Every non-2xx response returns a machine-readable OAuth2error code — branch on that field, not the message string.
error | Meaning |
|---|---|
invalid_request | Missing/malformed parameter. |
invalid_client | Unknown client_id, bad client_secret, or client_credentials requested on a public app. |
invalid_redirect_uri | redirect_uri doesn’t match any URI registered on the app. |
invalid_grant | Code/refresh token expired, already used, or PKCE code_verifier mismatch — restart the flow. |
invalid_scope | No scope requested and the app has no default_scopes. |
unsupported_grant_type | grant_type isn’t one of the three supported values. |
access_denied | User declined the consent screen. |
permission_denied | App-management endpoints only (not the token endpoint) — e.g. OAuth2 app management isn’t enabled for the account. |
server_error | Unexpected server-side failure. |
- TypeScript
- Python
- Curl
Wrap the raw
fetch call once and throw a typed error carrying the error code, so every caller can branch on err.code instead of string-matching: