Building on Untitled Sandbox
Register an app in Developer workspace, add a redirect URI, and send players through the standard OAuth 2.0 authorization-code flow with PKCE. Every scope is read-only. A token never shows more than the player themselves could see.
Endpoints
Authorization
https://untitled-sandbox.com/oauth/authorizeSend the player here. GET, response_type=code, PKCE S256.
Token
https://untitled-sandbox.com/api/oauth/tokenPOST form-encoded. authorization_code and refresh_token grants.
Revocation
https://untitled-sandbox.com/api/oauth/revokePOST a token to revoke it. Revoking a refresh token ends the grant.
Introspection
https://untitled-sandbox.com/api/oauth/introspectPOST a token; RFC 7662 answer.
Userinfo
https://untitled-sandbox.com/api/v1/userinfoGET with a bearer token. Needs the identity scope.
Server metadata
https://untitled-sandbox.com/.well-known/oauth-authorization-serverRFC 8414 discovery document.
OpenID configuration
https://untitled-sandbox.com/.well-known/openid-configurationOIDC discovery. id_token is ES256.
JWKS
https://untitled-sandbox.com/.well-known/jwks.jsonKeys that sign id_token.
Scopes
Open scopes come with registration. The rest need staff approval,
requested from the app's editor. identity
is always included.
identityOpenYour user id, username and avatar headshot.
openidOpenSign you in with your Untitled Sandbox account.
emailOpenYour verified email address.
profile.readOpenYour public profile: blurb, bio, join date, badges and showcase.
friends.readOpenWho your friends are.
clubs.readOpenWhich clubs you belong to, and your role in each.
forum.readOpenThe forum posts and replies you have written.
offline_accessOpenKeep reading the above while you are away from the site.
inventory.readNeeds approvalThe items you own, including serials. Not your vaults.
wallet.readNeeds approvalYour currency balances.
transactions.readNeeds approvalYour transaction history.
trading.readNeeds approvalYour trade history and open offers.
notifications.readNeeds approvalYour notifications.
The flow
1. Send the player to the authorization endpoint.
https://untitled-sandbox.com/oauth/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://example.com/callback
&scope=identity%20profile.read
&state=RANDOM_STATE
&code_challenge=BASE64URL_SHA256_OF_VERIFIER
&code_challenge_method=S256
2. They come back to your redirect URI with code and state. Exchange the code within a few minutes.
curl -X POST 'https://untitled-sandbox.com/api/oauth/token' \
-u 'YOUR_CLIENT_ID:YOUR_CLIENT_SECRET' \
-d 'grant_type=authorization_code' \
-d 'code=THE_CODE_FROM_THE_REDIRECT' \
-d 'redirect_uri=https://example.com/callback' \
-d 'code_verifier=THE_PKCE_VERIFIER_YOU_GENERATED'
# Public apps drop -u and send client_id in the body instead.
3. Call the API with the access token. It lasts an hour; use the
refresh token (given when offline_access
was granted) to get another.
curl 'https://untitled-sandbox.com/api/v1/userinfo' \
-H 'Authorization: Bearer THE_ACCESS_TOKEN'Errors
JSON with error and, usually, error_description.
invalid_request400A parameter is missing, repeated or malformed.
invalid_client401Unknown client, wrong secret, or the app is not active.
invalid_grant400The code or refresh token is expired, used or revoked.
invalid_scope400A scope is unknown or not allowed for this app.
invalid_token401The bearer token is missing, expired or revoked.
insufficient_scope403The token lacks the scope this endpoint needs.
rate_limited429Slow down. Retry-After says how long.
server_error500Something went wrong on the server. Try again later.
Rate limits
Counted per client, not per IP. Every response carries X-RateLimit-Limit and X-RateLimit-Remaining.
The token endpoint is capped separately at 30 requests per minute per client.
STANDARD60 requests per minuteEvery new app.
ELEVATED600 requests per minuteOn request, once an app has a track record.
FIRST_PARTY6,000 requests per minuteUntitled Sandbox apps only.
Privacy
[Placeholder: the third-party data policy is not written yet.]