OAuth

Endpoint reference

Every /oauth/* endpoint, token format, error code, and rate limit.

Protocol endpoints live at the root of the issuer (https://mayaapi.teamcast.ai/oauth/*), not under /api/v1. The discovery document is the authoritative source of URLs.

Discovery

MethodEndpointDescription
GET/.well-known/oauth-authorization-serverRFC 8414 authorization server metadata
GET/.well-known/jwks.jsonRSA public keys for offline access-token verification
bash
curl https://mayaapi.teamcast.ai/.well-known/oauth-authorization-server | jq

Protocol endpoints

MethodEndpointAuthPurpose
GET/oauth/authorizebrowser sessionStart an authorization code flow
POST/oauth/tokenclient (basic/post/none)Exchange code or refresh; client_credentials
POST/oauth/introspectclient (basic/post)RFC 7662 — is this token active?
POST/oauth/revokeclient (basic/post/none)RFC 7009 — revoke an access or refresh token

Grants supported

GrantPurpose
authorization_code (+ PKCE S256)User login via browser ("Sign in with Teamcast")
refresh_tokenLong-lived access — rotating with reuse detection
client_credentialsServer-to-server, no user. CONFIDENTIAL clients only

GET /oauth/authorize

Required query params:

ParamDescription
response_typeMust be "code".
client_idYour registered client id.
redirect_uriExact match of one of the registered redirect URIs.
scopeSpace-delimited — subset of the client's registered scopes.
stateOpaque random string; echoed back (CSRF defense).
code_challengebase64url(SHA256(code_verifier)) — 43–128 chars.
code_challenge_methodMust be "S256". plain is rejected.

Optional:

ParamDescription
promptSpace-delimited subset of none | login | consent | select_account.
nonceReserved for OIDC — ignored today.
bash
https://mayaapi.teamcast.ai/oauth/authorize?response_type=code
  &client_id=<CID>
  &redirect_uri=https://partner.example.com/oauth/callback
  &scope=interview:read%20interview:create
  &state=<csrf-random>
  &code_challenge=<base64url-s256>
  &code_challenge_method=S256
  &prompt=select_account

POST /oauth/token

authorization_code

bash
curl -X POST https://mayaapi.teamcast.ai/oauth/token \
  -u "<CLIENT_ID>:<CLIENT_SECRET>" \
  -d "grant_type=authorization_code" \
  -d "code=<received>" \
  -d "redirect_uri=https://partner.example.com/oauth/callback" \
  -d "code_verifier=<original>"

refresh_token (rotating)

bash
curl -X POST https://mayaapi.teamcast.ai/oauth/token \
  -u "<CLIENT_ID>:<CLIENT_SECRET>" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=<old>"
The response always includes a new refresh_token — you must persist and use it on the next refresh. Presenting a rotated-out refresh token triggers full token-family revocation.

client_credentials

bash
curl -X POST https://mayaapi.teamcast.ai/oauth/token \
  -u "<CLIENT_ID>:<CLIENT_SECRET>" \
  -d "grant_type=client_credentials" \
  -d "scope=interview:read"

Token response (all grants):

json
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 900,
  "refresh_token": "opaque-string",
  "scope": "interview:read interview:create"
}

Access token — RS256 JWT

Signed with RS256, header typ=at+jwt. Verify offline with the JWKS.

json
{
  "iss": "https://mayaapi.teamcast.ai",
  "sub": "<userId> | client:<clientId>",
  "aud": "teamcast-api",
  "client_id": "<clientId>",
  "tenant_id": "<tenantId> | null",
  "scope": "interview:read interview:create",
  "jti": "<uuid>",
  "iat": 1776200000,
  "exp": 1776200900,
  "auth_time": 1776199980,
  "token_use": "access"
}

POST /oauth/introspect (RFC 7662)

Client-authenticated. Returns { active: false } for unknown, expired, revoked, or foreign-client tokens. PUBLIC clients may not introspect.

bash
curl -X POST https://mayaapi.teamcast.ai/oauth/introspect \
  -u "<CLIENT_ID>:<CLIENT_SECRET>" \
  -d "token=<access_or_refresh>"

POST /oauth/revoke (RFC 7009)

Client-authenticated. Revoking a refresh token revokes its entire family. Always returns 200 per spec.

bash
curl -X POST https://mayaapi.teamcast.ai/oauth/revoke \
  -u "<CLIENT_ID>:<CLIENT_SECRET>" \
  -d "token=<refresh_token>" \
  -d "token_type_hint=refresh_token"

User consent management

MethodEndpointAuthPurpose
GEThttps://mayaapi.teamcast.ai/api/v1/oauth/consentsuser JWTList the authenticated user's connected apps
DELETEhttps://mayaapi.teamcast.ai/api/v1/oauth/consents/:clientIduser JWTRevoke consent + cascade (tokens wiped)

Scopes and resource-server behavior

Each scope maps to a permission in the RBAC registry (interview:read, interview:create, etc). Endpoints that accept OAuth tokens carry @RequireScope(...); missing scopes → 403 with a Missing required scope(s) message.

Endpoints that do not declare @RequireScope reject OAuth tokens outright. Partner access is explicit, per-endpoint.

Error codes (RFC 6749)

CodeMeaning
invalid_requestMissing / malformed parameter
invalid_clientUnknown client_id or failed client authentication
invalid_grantCode / refresh unknown, expired, reused, or client mismatch
unauthorized_clientClient not allowed to use this grant
unsupported_response_typeOnly "code" is supported
invalid_scopeRequested scope not allowed for this client
access_deniedUser denied consent, or tenant-audience mismatch

Rate limits (per IP unless noted)

EndpointLimit
GET /oauth/authorize30 / min
POST /oauth/authorize/decision30 / min
POST /oauth/token60 / min
POST /oauth/introspect120 / min
POST /oauth/revoke60 / min
Was this page helpful?