OAuth 2.0
Xiaoya uses the authorization-code flow to grant a third-party agent the minimum permissions approved by the current user. Resource APIs revalidate the user, agent, scope, Team membership, and context version on every request.
Authorization request
GET /oauth/authorizeUse response_type=code, the registered client_id and exact redirect_uri, a space-separated scope, and a validated state. Public clients use PKCE S256. Team authorization should explicitly include the target team_id.
https://claw.zhenhuiai.com/oauth/authorize?response_type=code&client_id=agent-client&redirect_uri=https%3A%2F%2Fagent.example.com%2Foauth%2Fcallback&scope=profile%20balance%20model&state=<random-state>&code_challenge=<s256-challenge>&code_challenge_method=S256The Xiaoya App open flow attaches a single-use code to the registered entry URL. The code expires after five minutes. Do not render third-party resources before exchanging it; finish with a 303 redirect to a clean URL without the code.
Scopes
| Scope | Capability |
|---|---|
profile | Current subject and visible Team contexts |
balance | Personal or exact Team balance |
charge | Direct charge and refund |
usage | Agent-attributed Personal usage |
model | Model catalog and model gateway |
team_read | Team read access |
team_manage | Owner/Admin Team management |
skill | Licensed runtime assets |
sandbox | Cloud sandbox runtime |
The default scopes are only profile balance charge usage; model and Team clients must explicitly request the additional scopes they use.
Lifecycle and isolation
- Authorization code: five minutes, single use.
- New access token: 7 days. Previously issued tokens keep their original expiry. Developer sessions must not outlive the returned
expires_in. - New exchanges do not issue refresh tokens; legacy refresh tokens are compatibility only.
- A user, agent, membership, role, Team, or context-version change can invalidate existing credentials.
- Store Personal and Team sessions separately, keyed by client, subject, context type, and exact Team ID.
Exchange the code
Confidential server clients submit client_secret. Public clients do not store a secret and submit the matching PKCE code_verifier instead.
curl --request POST 'https://claw.zhenhuiai.com/oauth/token' \
--header 'Accept: application/json' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_id=agent-client' \
--data-urlencode 'client_secret=<server-only-secret>' \
--data-urlencode 'code=<single-use-code>' \
--data-urlencode 'redirect_uri=https://agent.example.com/oauth/callback'const body = new URLSearchParams({
grant_type: 'authorization_code',
client_id: process.env.XIAOYA_CLIENT_ID,
client_secret: process.env.XIAOYA_CLIENT_SECRET,
code,
redirect_uri: 'https://agent.example.com/oauth/callback'
});
const response = await fetch('https://claw.zhenhuiai.com/oauth/token', {
method: 'POST',
headers: { Accept: 'application/json', 'Content-Type': 'application/x-www-form-urlencoded' },
body,
redirect: 'error',
signal: AbortSignal.timeout(10_000)
});
const token = await response.json();
if (!response.ok) throw new Error(`OAuth failed: ${token.error}`);Personal token response:
{
"access_token": "<server-only-token>",
"token_type": "Bearer",
"expires_in": 604800,
"scope": "profile balance charge usage model"
}A Team token additionally contains its exact binding:
{
"access_token": "<server-only-team-token>",
"token_type": "Bearer",
"expires_in": 604800,
"scope": "profile balance model team_read",
"team_id": "team_01",
"team_authorization_id": "authorization_01"
}| Field | Meaning |
|---|---|
access_token | Bearer credential stored only on a trusted server |
token_type | Always Bearer |
expires_in | Lifetime in seconds; currently 604800 |
scope | Actually granted scopes, which may differ from those requested |
team_id | Exact Team binding, returned only for a Team token |
team_authorization_id | Revocable Team authorization record |
OAuth failures do not use the platform envelope:
{
"error": "invalid_grant",
"error_description": "Authorization code is invalid or expired"
}Common values are invalid_client, invalid_grant, and unsupported_grant_type.
Revoke a token
curl --request POST 'https://claw.zhenhuiai.com/oauth/revoke' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=agent-client' \
--data-urlencode 'client_secret=<server-only-secret>' \
--data-urlencode 'token=<access-token>' \
--data-urlencode 'token_type_hint=access_token'A successful revocation returns HTTP 200. Clear the developer session even if the token was already inactive.
Never place a token, code, or client secret in browser storage, a URL, an AI prompt, logs, or error details. Browser code should hold only the agent application's own secure HttpOnly session cookie.
See the complete API reference for every OAuth parameter and response schema.
