OAuth 2.0
StudioBase implements the OAuth 2.0 authorization code grant (RFC 6749). A studio owner authorizes your application once; your application then holds tokens scoped to that single studio.
Client registration is invite-only. The StudioBase team issues your
client_id and client_secret and registers your exact redirect URIs.
Authorization endpoint
GET https://www.studiobase.org/oauth/authorize
Redirect the user's browser here to start the flow.
| Query parameter | Required | Description |
|---|---|---|
client_id | Yes | Your client identifier |
redirect_uri | Yes | Must exactly match a registered redirect URI (no prefix or wildcard matching) |
response_type | Yes | Must be code |
state | Recommended | Opaque value echoed back to you unchanged — use it for CSRF protection |
code_challenge | Recommended | PKCE challenge (base64url encoded SHA-256 hash of the code_verifier). Required for public clients. |
code_challenge_method | Recommended | Must be S256 if code_challenge is provided. |
The user signs in with their StudioBase owner account (the request round-trips through login if needed), picks which studio to connect when they own more than one, and approves or denies the request. Authorizing also enables webhooks for the selected studio.
Approved: the browser is redirected to your redirect_uri with
code (an authorization code, prefix sbac_) and your state:
https://your.app/callback?code=sbac_…&state=…
Denied: the redirect carries error=access_denied and your state.
Authorization codes are single-use and expire after 10 minutes.
If client_id, redirect_uri, or response_type is missing or invalid,
the error is rendered inline and the browser is not redirected
(RFC 6749 §4.1.2.1).
Token endpoint
POST https://www.studiobase.org/api/oauth/token
Content-Type: application/x-www-form-urlencoded
Client credentials are sent in the request body (client_id +
client_secret). HTTP Basic client authentication is not supported.
Exchange an authorization code
| Body field | Required | Description |
|---|---|---|
grant_type | Yes | authorization_code |
code | Yes | The code from the redirect |
redirect_uri | Yes | The same redirect_uri used in the authorization request |
client_id | Yes | Your client identifier |
client_secret | Required for confidential clients | Your client secret |
code_verifier | Recommended | The PKCE verifier matching the code_challenge from the authorization request. Required if a challenge was provided. |
curl -X POST https://www.studiobase.org/api/oauth/token \
-d grant_type=authorization_code \
-d code=sbac_… \
-d redirect_uri=https://your.app/callback \
-d client_id=your_client_id \
-d client_secret=your_client_secret # Omit for public clients
Success (200):
{
"access_token": "sbat_…",
"refresh_token": "sbrt_…",
"token_type": "bearer",
"expires_in": 3600
}
Token responses are sent with Cache-Control: no-store. Store both tokens
securely — StudioBase stores only hashes and cannot show them again.
Refresh an access token
| Body field | Required | Description |
|---|---|---|
grant_type | Yes | refresh_token |
refresh_token | Yes | The refresh token from the original exchange |
client_id | Yes | Your client identifier |
client_secret | Required for confidential clients | Your client secret |
Success (200): same shape as above, with a new access_token. The
refresh token does not rotate — the response echoes the same
refresh_token.
Access tokens expire after 3600 seconds (1 hour). Use the access token
as Authorization: Bearer sbat_… against the
Webhook Subscription API.
Error responses
Errors use RFC 6749 error codes in a JSON body, e.g.
{"error": "invalid_grant"}.
| HTTP | error | When |
|---|---|---|
| 400 | invalid_request | Body is not parseable form-encoded data |
| 400 | invalid_grant | Unknown, expired, or already-used code; redirect_uri mismatch; unknown or revoked refresh token |
| 400 | unsupported_grant_type | grant_type is not authorization_code or refresh_token |
| 401 | invalid_client | Missing or unknown client_id, or wrong client_secret |
| 429 | temporarily_unavailable | Rate limited (30 requests/min per IP) — honor Retry-After |
| 500 | server_error | Transient server failure — safe to retry |
Revocation
- Code replay defense: exchanging an authorization code a second time
fails with
invalid_grantand revokes the connection minted from that code, in case the code leaked. - Studio-initiated disconnect: studio owners can disconnect your
application at any time from Settings → Integrations → Webhooks.
Revocation immediately invalidates the connection's tokens (subsequent
API calls return
401 {"error": "invalid_token"}) and deactivates its webhook subscriptions.