From 0e63e2e2505153a0c09f2c1c159531751f072f4b Mon Sep 17 00:00:00 2001 From: Aofei Sheng Date: Wed, 20 May 2026 09:12:23 +0800 Subject: [PATCH] docs(openapi): add XBuilder Account API spec Define the OpenAPI contract for XBuilder Account as the source of truth for replacing Casdoor. Cover hosted sign-in, Account OAuth, Account-issued app-scoped OAuth tokens, account sessions, account user management, admin APIs, app management, audit logs, and authorization boundaries. Updates #3112 Signed-off-by: Aofei Sheng --- docs/openapi.yaml | 7514 +++++++++++++++++++++++++++++---------------- 1 file changed, 4893 insertions(+), 2621 deletions(-) diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 8fd5dd393..63129c21d 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -12,127 +12,29 @@ security: - bearerAuth: [] paths: - /user: - get: - operationId: getAuthenticatedUser - tags: - - Users - summary: Get the authenticated user - description: Retrieve the profile information of the authenticated user. - responses: - "200": - description: Successful response. - content: - application/json: - schema: - $ref: "#/components/schemas/AuthenticatedUser" - "401": - $ref: "#/components/responses/Unauthorized" - patch: - operationId: updateAuthenticatedUser - tags: - - Users - summary: Update the authenticated user - description: Update the profile information of the authenticated user. - requestBody: - required: true - content: - application/json: - schema: - type: object - minProperties: 1 - properties: - username: - description: New username of the authenticated user. If provided, the username will be modified. - $ref: "#/components/schemas/User/properties/username" - displayName: - $ref: "#/components/schemas/User/properties/displayName" - avatar: - description: New avatar Kodo universal URL of the authenticated user. - allOf: - - $ref: "#/components/schemas/User/properties/avatar" - - minLength: 1 - pattern: "^kodo://[^/?#@:]+/[^?#]+$" - examples: - - kodo://xbuilder-usercontent-test/files/FvYyHLNrtx8qFHSwnLjEe57UA2fF-2824936 - description: - $ref: "#/components/schemas/User/properties/description" - responses: - "200": - description: Successfully updated the authenticated user. - content: - application/json: - schema: - $ref: "#/components/schemas/AuthenticatedUser" - "401": - $ref: "#/components/responses/Unauthorized" - - /user/followers: + /account/identity-providers: get: - operationId: listAuthenticatedUserFollowers + operationId: listAccountIdentityProviders tags: - - Users - summary: List followers of the authenticated user - description: Retrieve users who follow the authenticated user. + - Account + summary: List identity providers + description: Retrieve identity providers available for hosted sign-in. + security: [] parameters: - - name: orderBy - description: Field by which to order the results. + - name: clientID + description: OAuth client ID of the app requesting hosted sign-in. in: query + required: true schema: - type: string - enum: - - createdAt - - updatedAt - - followedAt - default: followedAt - examples: - - followedAt - - $ref: "#/components/parameters/SortOrder" - - $ref: "#/components/parameters/PageIndex" - - $ref: "#/components/parameters/PageSize" - responses: - "200": - description: Successful response. - content: - application/json: - schema: - type: object - required: - - total - - data - properties: - total: - $ref: "#/components/schemas/ByPage/properties/total" - data: - type: array - items: - $ref: "#/components/schemas/User" - "401": - $ref: "#/components/responses/Unauthorized" - - /user/following: - get: - operationId: listAuthenticatedUserFollowing - tags: - - Users - summary: List users followed by the authenticated user - description: Retrieve users followed by the authenticated user. - parameters: - - name: orderBy - description: Field by which to order the results. + $ref: "#/components/schemas/OAuthClientID" + - name: requestURI + description: Pushed authorization request URI returned by `/account/oauth/par`. in: query + required: true schema: type: string - enum: - - createdAt - - updatedAt - - followedAt - default: followedAt - examples: - - followedAt - - $ref: "#/components/parameters/SortOrder" - - $ref: "#/components/parameters/PageIndex" - - $ref: "#/components/parameters/PageSize" + format: uri + minLength: 1 responses: "200": description: Successful response. @@ -141,587 +43,487 @@ paths: schema: type: object required: - - total - data properties: - total: - $ref: "#/components/schemas/ByPage/properties/total" data: type: array items: - $ref: "#/components/schemas/User" - "401": - $ref: "#/components/responses/Unauthorized" + $ref: "#/components/schemas/AccountIdentityProvider" - /user/following/{username}: + /account/identity-providers/{provider}/authorize: get: - operationId: checkAuthenticatedUserFollowing + operationId: authorizeAccountIdentityProvider tags: - - Users - summary: Check whether the authenticated user follows a user - description: Determine if the authenticated user follows the specified user. + - Account + summary: Start identity provider authorization + description: Redirect the user to the provider authorization endpoint for the current hosted sign-in flow. + security: [] parameters: - - name: username - description: Username of the user to check. + - name: provider + description: Identity provider name. in: path required: true schema: - $ref: "#/components/schemas/User/properties/username" - responses: - "204": - description: The authenticated user follows the specified user. - "301": - $ref: "#/components/responses/MovedPermanently" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - description: The target user does not exist, or the authenticated user does not follow the target user. - put: - operationId: followUser - tags: - - Users - summary: Follow a user - description: Add the specified user to the authenticated user's following collection. - parameters: - - name: username - description: Username of the user to follow. - in: path + $ref: "#/components/schemas/AccountIdentityProvider/properties/name" + - name: clientID + description: OAuth client ID of the app requesting hosted sign-in. + in: query required: true schema: - $ref: "#/components/schemas/User/properties/username" - responses: - "204": - description: Successfully followed the user, or the relationship already existed. - "401": - $ref: "#/components/responses/Unauthorized" - "409": - $ref: "#/components/responses/ResourceMoved" - delete: - operationId: unfollowUser - tags: - - Users - summary: Unfollow a user - description: Remove the specified user from the authenticated user's following collection. - parameters: - - name: username - description: Username of the user to unfollow. - in: path + $ref: "#/components/schemas/OAuthClientID" + - name: requestURI + description: Pushed authorization request URI returned by `/account/oauth/par`. + in: query required: true schema: - $ref: "#/components/schemas/User/properties/username" + type: string + format: uri + minLength: 1 responses: - "204": - description: Successfully unfollowed the user, or the relationship did not exist. - "401": - $ref: "#/components/responses/Unauthorized" - "409": - $ref: "#/components/responses/ResourceMoved" + "302": + description: Redirect to the identity provider authorization endpoint. + headers: + Location: + description: Provider authorization URL. + schema: + type: string + format: uri + minLength: 1 + "400": + description: Invalid hosted sign-in request. - /user/projects: + /account/identity-providers/{provider}/callback: get: - operationId: listAuthenticatedUserProjects + operationId: handleAccountIdentityProviderCallback tags: - - Projects - summary: List projects owned by the authenticated user - description: Retrieve a paginated list of projects owned by the authenticated user. + - Account + summary: Handle identity provider callback + description: | + Handle the identity provider callback for hosted sign-in. Callback state is used to associate the provider + response with the hosted sign-in flow and provide CSRF protection. The provider callback includes either an + authorization code or an OAuth error. + security: [] parameters: - - name: remixedFrom - description: | - Filter remixed projects by the full name of the source project or project release. - - Moved project or project release names are transparently normalized to canonical values. - in: query - schema: - oneOf: - - $ref: "#/components/schemas/ProjectFullName" - - $ref: "#/components/schemas/ProjectReleaseFullName" - - name: keyword - description: Filter projects by display name or name pattern. - in: query - schema: - type: string - examples: - - Niu Xiao Qi - - name: visibility - description: Filter projects by visibility. - in: query - schema: - $ref: "#/components/schemas/Project/properties/visibility" - - name: type - description: Filter projects by project type. - in: query - schema: - $ref: "#/components/schemas/Project/properties/type" - - name: createdAfter - description: Filter projects created after this timestamp. - in: query + - name: provider + description: Identity provider name. + in: path + required: true schema: - type: string - format: date-time - - name: likesReceivedAfter - description: Filter projects that gained new likes after this timestamp. + $ref: "#/components/schemas/AccountIdentityProvider/properties/name" + - name: code + description: Provider authorization code. in: query schema: type: string - format: date-time - - name: remixesReceivedAfter - description: Filter projects that were remixed after this timestamp. + minLength: 1 + - name: state + description: Provider callback state. in: query + required: true schema: type: string - format: date-time - - name: orderBy - description: Field by which to order the results. + minLength: 1 + - name: error + description: Provider error code. in: query schema: type: string - enum: - - createdAt - - updatedAt - - likeCount - - remixCount - - recentLikeCount - - recentRemixCount - default: createdAt - examples: - - createdAt - - $ref: "#/components/parameters/SortOrder" - - $ref: "#/components/parameters/PageIndex" - - $ref: "#/components/parameters/PageSize" + minLength: 1 responses: - "200": - description: Successful response. - content: - application/json: - schema: - type: object - required: - - total - - data - properties: - total: - $ref: "#/components/schemas/ByPage/properties/total" - data: - type: array - items: - $ref: "#/components/schemas/Project" - "401": - $ref: "#/components/responses/Unauthorized" + "302": + description: Redirect back to hosted sign-in. + headers: + Location: + description: Hosted sign-in URL. + schema: + type: string + format: uri + minLength: 1 + Set-Cookie: + description: | + Sets the `__Host-xbuilder-account-session` cookie when the provider callback creates an account session. + schema: + type: string post: - operationId: createProject + operationId: submitAccountIdentityProviderCallback tags: - - Projects - summary: Create a project + - Account + summary: Submit identity provider callback description: | - Create a new project owned by the authenticated user. - - If `remixSource` is provided, the new project will be a remix of an existing project. + Handle identity provider callbacks that use form post response mode. These external callbacks use the OAuth + state parameter for CSRF protection instead of a same-site web CSRF token. The provider callback includes either + an authorization code or an OAuth error. + security: [] + parameters: + - name: provider + description: Identity provider name. + in: path + required: true + schema: + $ref: "#/components/schemas/AccountIdentityProvider/properties/name" requestBody: required: true content: - application/json: + application/x-www-form-urlencoded: schema: type: object required: - - name - - visibility + - state + anyOf: + - required: + - code + - required: + - error properties: - remixSource: - description: | - Full name of the project or project release to remix from. - - Using the project's full name means remixing the latest release sourced from that project. - - Moved project or project release names return `40901` with a `canonical` payload. - oneOf: - - $ref: "#/components/schemas/ProjectFullName" - - $ref: "#/components/schemas/ProjectReleaseFullName" - name: - $ref: "#/components/schemas/Project/properties/name" - displayName: - description: Defaults to `name` if not provided. - $ref: "#/components/schemas/Project/properties/displayName" - type: - description: Defaults to `game` if not provided. - $ref: "#/components/schemas/Project/properties/type" - files: - description: | - File paths and their corresponding universal URLs associated with the project. - - Required if `remixSource` is not provided. - $ref: "#/components/schemas/Project/properties/files" - visibility: - $ref: "#/components/schemas/Project/properties/visibility" - description: - $ref: "#/components/schemas/Project/properties/description" - instructions: - $ref: "#/components/schemas/Project/properties/instructions" - extraSettings: - $ref: "#/components/schemas/Project/properties/extraSettings" - thumbnail: - $ref: "#/components/schemas/Project/properties/thumbnail" + code: + description: Provider authorization code. + type: string + minLength: 1 + state: + description: Provider callback state. + type: string + minLength: 1 + error: + description: Provider error code. + type: string + minLength: 1 responses: - "201": - description: Successfully created the project. + "302": + description: Redirect back to hosted sign-in. headers: Location: - description: Canonical route of the created project. + description: Hosted sign-in URL. schema: type: string - format: uri-reference + format: uri minLength: 1 - examples: - - /projects/john/NiuXiaoQi - content: - application/json: + Set-Cookie: + description: | + Sets the `__Host-xbuilder-account-session` cookie when the provider callback creates an account session. schema: - $ref: "#/components/schemas/Project" - "401": - $ref: "#/components/responses/Unauthorized" - "409": - description: | - Returned when `remixSource` references a moved project or project release. + type: string - The response includes a `canonical` payload for the route-derived fields in `remixSource`. + /account/oauth/par: + post: + operationId: createAccountOAuthPushedAuthorizationRequest + tags: + - Account OAuth + summary: Create a pushed authorization request + description: | + Create a pushed authorization request for an app using OAuth 2.0 PAR. - Clients may update the request and resubmit it explicitly. + The requested redirect URI must exactly match the app allowlist. Provider credential handoff codes, when supplied, + must be valid, unexpired, unused, and match the requested provider. Invalid provider codes must not produce usable + `request_uri` values. + + The returned `request_uri` is an opaque, short-lived, single-use reference and is not a dereferenceable URL. + security: + - oauthClientSecretBasic: [] + - {} + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + type: object + allOf: + - if: + required: + - xbuilder_provider_code + then: + required: + - xbuilder_provider + - if: + required: + - xbuilder_provider + then: + required: + - xbuilder_provider_code + required: + - response_type + - client_id + - redirect_uri + - state + - code_challenge + - code_challenge_method + - scope + properties: + response_type: + description: OAuth response type. + type: string + enum: + - code + client_id: + description: OAuth client ID of the app. + $ref: "#/components/schemas/OAuthClientID" + redirect_uri: + description: Redirect URI requested by the app. + type: string + format: uri + state: + description: OAuth state value. + type: string + minLength: 1 + code_challenge: + $ref: "#/components/schemas/OAuthPKCECodeChallenge" + code_challenge_method: + description: PKCE code challenge method. + type: string + enum: + - S256 + scope: + description: OAuth scope requested by the app. + allOf: + - $ref: "#/components/schemas/OAuthScope" + xbuilder_provider: + description: Identity provider used for provider credential handoff. + $ref: "#/components/schemas/AccountIdentityProvider/properties/name" + xbuilder_provider_code: + description: Short-lived provider code obtained by the app frontend. + type: string + minLength: 1 + responses: + "201": + description: Pushed authorization request created. content: application/json: schema: - $ref: "#/components/schemas/MovedResourceError" + $ref: "#/components/schemas/OAuthPushedAuthorizationResponse" + "400": + $ref: "#/components/responses/OAuthError" + "401": + $ref: "#/components/responses/OAuthInvalidClient" - /user/liked-projects: + /account/oauth/authorize: get: - operationId: listAuthenticatedUserLikedProjects + operationId: authorizeAccountOAuthApp tags: - - Projects - summary: List projects liked by the authenticated user - description: Retrieve projects liked by the authenticated user. - parameters: - - name: remixedFrom - description: | - Filter remixed projects by the full name of the source project or project release. + - Account OAuth + summary: Authorize an app + description: | + Start or complete the OAuth authorization code flow for a registered app. - Moved project or project release names are transparently normalized to canonical values. - in: query - schema: - oneOf: - - $ref: "#/components/schemas/ProjectFullName" - - $ref: "#/components/schemas/ProjectReleaseFullName" - - name: keyword - description: Filter projects by display name or name pattern. - in: query - schema: - type: string - examples: - - Niu Xiao Qi - - name: visibility - description: Filter projects by visibility. - in: query - schema: - $ref: "#/components/schemas/Project/properties/visibility" - - name: type - description: Filter projects by project type. - in: query - schema: - $ref: "#/components/schemas/Project/properties/type" - - name: createdAfter - description: Filter projects created after this timestamp. - in: query - schema: - type: string - format: date-time - - name: likesReceivedAfter - description: Filter projects that gained new likes after this timestamp. - in: query - schema: - type: string - format: date-time - - name: remixesReceivedAfter - description: Filter projects that were remixed after this timestamp. + This endpoint accepts PAR authorization requests only. The app must push authorization request parameters to + `/account/oauth/par` first and then call this endpoint with `client_id` and `request_uri`. + + The `request_uri` value must reference a valid pushed authorization request bound to the `client_id`. + + If no valid account session is available or hosted interaction is required, this endpoint redirects to hosted + sign-in. After account and app authorization is resolved, the authorization response redirects to the app + callback with an authorization code and the original `state`, or with OAuth error parameters and the original + `state`. + security: [] + parameters: + - name: client_id + description: OAuth client ID of the app. in: query + required: true schema: - type: string - format: date-time - - name: orderBy - description: Field by which to order the results. + $ref: "#/components/schemas/OAuthClientID" + - name: request_uri + description: Opaque pushed authorization request URI returned by `/account/oauth/par`. in: query + required: true schema: type: string - enum: - - createdAt - - updatedAt - - likeCount - - remixCount - - recentLikeCount - - recentRemixCount - - likedAt - default: likedAt + format: uri + minLength: 1 examples: - - likedAt - - $ref: "#/components/parameters/SortOrder" - - $ref: "#/components/parameters/PageIndex" - - $ref: "#/components/parameters/PageSize" + - urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY22c + responses: + "302": + description: Redirect to the app callback with an authorization code or OAuth error parameters. + headers: + Location: + description: App callback URL. + schema: + type: string + format: uri + minLength: 1 + Set-Cookie: + description: | + Sets the `__Host-xbuilder-account-session` cookie when provider credential handoff creates an account + session. + schema: + type: string + "400": + $ref: "#/components/responses/OAuthError" + + /account/oauth/token: + post: + operationId: createAccountOAuthToken + tags: + - Account OAuth + summary: Create an OAuth token + description: | + Create or refresh Account-issued app-scoped OAuth tokens. + + Authorization code exchange validates the redirect URI and PKCE verifier. Refresh token exchange rotates the + refresh token. Reuse of a rotated refresh token is treated as a replay signal and revokes the corresponding token + family or grant. + security: + - oauthClientSecretBasic: [] + - {} + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/CreateOAuthTokenRequest" responses: "200": description: Successful response. content: application/json: schema: - type: object - required: - - total - - data - properties: - total: - $ref: "#/components/schemas/ByPage/properties/total" - data: - type: array - items: - $ref: "#/components/schemas/Project" + $ref: "#/components/schemas/OAuthToken" + "400": + $ref: "#/components/responses/OAuthError" "401": - $ref: "#/components/responses/Unauthorized" + $ref: "#/components/responses/OAuthInvalidClient" - /user/liked-projects/{owner}/{project}: - get: - operationId: checkAuthenticatedUserLikedProject + /account/oauth/introspect: + post: + operationId: introspectAccountOAuthToken tags: - - Projects - summary: Check whether the authenticated user liked a project - description: Determine if the authenticated user has liked the specified project. - parameters: - - name: owner - description: Username of the project's owner. - in: path - required: true - schema: - $ref: "#/components/schemas/Project/properties/owner" - - name: project - description: Name of the project to check. - in: path - required: true - schema: - $ref: "#/components/schemas/Project/properties/name" - responses: - "204": - description: The project is liked by the authenticated user. - "301": - $ref: "#/components/responses/MovedPermanently" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - description: The project does not exist, or the authenticated user has not liked the project. - put: - operationId: likeProject - tags: - - Projects - summary: Like a project - description: Add the project to the authenticated user's liked-projects collection. - parameters: - - name: owner - description: Username of the project's owner. - in: path - required: true - schema: - $ref: "#/components/schemas/Project/properties/owner" - - name: project - description: Name of the project to like. - in: path - required: true - schema: - $ref: "#/components/schemas/Project/properties/name" + - Account OAuth + summary: Introspect an OAuth token + description: Introspect an Account-issued OAuth token using OAuth 2.0 Token Introspection. + security: + - oauthClientSecretBasic: [] + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + type: object + required: + - token + properties: + token: + description: OAuth token to introspect. + type: string + minLength: 1 + token_type_hint: + description: | + Hint about the type of token submitted for introspection. + + Known values include `access_token` and `refresh_token`. + type: string + minLength: 1 + examples: + - access_token responses: - "204": - description: Successfully liked the project, or the relationship already existed. + "200": + description: Successful response. + content: + application/json: + schema: + $ref: "#/components/schemas/OAuthIntrospection" + "400": + $ref: "#/components/responses/OAuthError" "401": - $ref: "#/components/responses/Unauthorized" - "409": - $ref: "#/components/responses/ResourceMoved" - delete: - operationId: unlikeProject + $ref: "#/components/responses/OAuthInvalidClient" + + /account/oauth/revoke: + post: + operationId: revokeAccountOAuthToken tags: - - Projects - summary: Unlike a project - description: Remove the project from the authenticated user's liked-projects collection. - parameters: - - name: owner - description: Username of the project's owner. - in: path - required: true - schema: - $ref: "#/components/schemas/Project/properties/owner" - - name: project - description: Name of the project to unlike. - in: path - required: true - schema: - $ref: "#/components/schemas/Project/properties/name" + - Account OAuth + summary: Revoke an OAuth token + description: | + Revoke an Account-issued OAuth token using OAuth 2.0 Token Revocation. + + Confidential clients authenticate with `client_secret_basic` and must not send `client_id` in the request body. + Public clients must send `client_id`. + security: + - oauthClientSecretBasic: [] + - {} + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/RevokeOAuthTokenRequest" responses: - "204": - description: Successfully unliked the project, or the relationship did not exist. + "200": + description: OAuth token revoked. + "400": + $ref: "#/components/responses/OAuthError" "401": - $ref: "#/components/responses/Unauthorized" - "409": - $ref: "#/components/responses/ResourceMoved" + $ref: "#/components/responses/OAuthInvalidClient" - /user/assets: + /account/user: get: - operationId: listAuthenticatedUserAssets + operationId: getAccountUser tags: - - Assets - summary: List assets owned by the authenticated user - description: Retrieve a paginated list of assets owned by the authenticated user. - parameters: - - name: keyword - description: Filter assets by display name pattern. - in: query - schema: - $ref: "#/components/schemas/Asset/properties/displayName" - - name: type - description: Filter assets by type. - in: query - schema: - $ref: "#/components/schemas/Asset/properties/type" - - name: category - description: Filter assets by category. - in: query - schema: - $ref: "#/components/schemas/Asset/properties/category" - - name: filesHash - description: Filter assets by files hash. - in: query - schema: - $ref: "#/components/schemas/Asset/properties/filesHash" - - name: visibility - description: Filter assets by visibility. - in: query - schema: - $ref: "#/components/schemas/Asset/properties/visibility" - - name: orderBy - description: Field by which to order the results. - in: query - schema: - type: string - enum: - - createdAt - - updatedAt - - displayName - default: createdAt - examples: - - createdAt - - $ref: "#/components/parameters/SortOrder" - - $ref: "#/components/parameters/PageIndex" - - $ref: "#/components/parameters/PageSize" + - Account + summary: Get the current account user + description: | + Retrieve user fields exposed by XBuilder Account for the current user. + + Account Web may authenticate with an account session cookie. + + App backends may authenticate with an Account-issued app-scoped OAuth access token with the + `account:user:read` Account API scope. + security: + - accountSessionCookie: [] + - bearerAuth: [] responses: "200": description: Successful response. content: application/json: schema: - type: object - required: - - total - - data - properties: - total: - $ref: "#/components/schemas/ByPage/properties/total" - data: - type: array - items: - $ref: "#/components/schemas/Asset" + $ref: "#/components/schemas/AccountUser" "401": - $ref: "#/components/responses/Unauthorized" - post: - operationId: createAsset + $ref: "#/components/responses/OAuthInvalidBearerToken" + "403": + $ref: "#/components/responses/OAuthInsufficientScope" + patch: + operationId: updateAccountUser tags: - - Assets - summary: Create an asset + - Account + summary: Update the current account user description: | - Create a new asset owned by the authenticated user. - - Permission requirements: - - - Only users with the `assetAdmin` role can create public assets. - - Regular users can only create private assets. + Update user fields managed by XBuilder Account for the current user. Cookie-authenticated mutations must validate + Origin or use equivalent CSRF protection. + security: + - accountSessionCookie: [] requestBody: required: true content: application/json: schema: type: object - required: - - displayName - - type - - category - - description - - extraSettings - - files - - filesHash - - visibility + minProperties: 1 properties: + username: + $ref: "#/components/schemas/AccountUser/properties/username" displayName: - $ref: "#/components/schemas/Asset/properties/displayName" - type: - $ref: "#/components/schemas/Asset/properties/type" - category: - $ref: "#/components/schemas/Asset/properties/category" - description: - $ref: "#/components/schemas/Asset/properties/description" - extraSettings: - $ref: "#/components/schemas/Asset/properties/extraSettings" - files: - $ref: "#/components/schemas/Asset/properties/files" - filesHash: - $ref: "#/components/schemas/Asset/properties/filesHash" - visibility: - $ref: "#/components/schemas/Asset/properties/visibility" + $ref: "#/components/schemas/AccountUser/properties/displayName" + avatar: + $ref: "#/components/schemas/AccountUser/properties/avatar" responses: - "201": - description: Successfully created the asset. - headers: - Location: - description: Canonical route of the created asset. - schema: - type: string - format: uri-reference - minLength: 1 - examples: - - /assets/1 + "200": + description: Account user updated. content: application/json: schema: - $ref: "#/components/schemas/Asset" + $ref: "#/components/schemas/AccountUser" "401": $ref: "#/components/responses/Unauthorized" - "403": - description: Insufficient permissions to perform this operation. - /user/courses: + /account/user/identities: get: - operationId: listAuthenticatedUserCourses + operationId: listAccountUserIdentities tags: - - Courses - summary: List courses owned by the authenticated user - description: Retrieve courses owned by the authenticated user. + - Account + summary: List current account user identities + description: Retrieve a list of third-party identities linked to the current user. + security: + - accountSessionCookie: [] parameters: - - name: courseSeriesID - description: Filter courses by the course series ID. - in: query - schema: - $ref: "#/components/schemas/Model/properties/id" - - name: orderBy - description: Field by which to order the results. - in: query - schema: - type: string - enum: - - createdAt - - updatedAt - - sequenceInCourseSeries - default: createdAt - examples: - - createdAt - - $ref: "#/components/parameters/SortOrder" - $ref: "#/components/parameters/PageIndex" - $ref: "#/components/parameters/PageSize" responses: @@ -740,180 +542,101 @@ paths: data: type: array items: - $ref: "#/components/schemas/Course" + $ref: "#/components/schemas/AccountUserIdentity" "401": $ref: "#/components/responses/Unauthorized" + + /account/session: post: - operationId: createCourse + operationId: createAccountSession tags: - - Courses - summary: Create a course - description: | - Create a new course owned by the authenticated user. - - Permission requirements: - - - Only users with the `courseAdmin` role can create courses. + - Account + summary: Create an account session + description: Create an account session for hosted sign-in. + security: [] requestBody: required: true content: application/json: schema: - type: object - required: - - title - - thumbnail - - entrypoint - - references - - prompt - properties: - title: - $ref: "#/components/schemas/Course/properties/title" - thumbnail: - $ref: "#/components/schemas/Course/properties/thumbnail" - entrypoint: - $ref: "#/components/schemas/Course/properties/entrypoint" - references: - $ref: "#/components/schemas/Course/properties/references" - prompt: - $ref: "#/components/schemas/Course/properties/prompt" + $ref: "#/components/schemas/CreateAccountSessionRequest" + examples: + password: + summary: Password sign-in + value: + method: password + username: john + password: + providerCode: + summary: Provider credential handoff + value: + method: providerCode + provider: github + providerCode: provider-auth-code responses: "201": - description: Successfully created the course. + description: Account session created. headers: - Location: - description: Canonical route of the created course. + Set-Cookie: + description: | + Sets the `__Host-xbuilder-account-session` cookie with `HttpOnly`, `Secure`, `SameSite=Lax`, `Path=/`, + and no `Domain`. schema: type: string - format: uri-reference - minLength: 1 - examples: - - /courses/1 content: application/json: schema: - $ref: "#/components/schemas/Course" + $ref: "#/components/schemas/CurrentAccountSession" "401": $ref: "#/components/responses/Unauthorized" - "403": - description: Insufficient permissions to perform this operation. - - /user/course-series: get: - operationId: listAuthenticatedUserCourseSeries + operationId: getAccountSession tags: - - Course Series - summary: List course series owned by the authenticated user - description: Retrieve course series owned by the authenticated user. - parameters: - - name: orderBy - description: Field by which to order the results. - in: query - schema: - type: string - enum: - - createdAt - - updatedAt - - order - default: order - examples: - - order - - $ref: "#/components/parameters/SortOrder" - - $ref: "#/components/parameters/PageIndex" - - $ref: "#/components/parameters/PageSize" + - Account + summary: Get the current account session + description: Retrieve the current account session. + security: + - accountSessionCookie: [] responses: "200": description: Successful response. content: application/json: schema: - type: object - required: - - total - - data - properties: - total: - $ref: "#/components/schemas/ByPage/properties/total" - data: - type: array - items: - $ref: "#/components/schemas/CourseSeries" + $ref: "#/components/schemas/CurrentAccountSession" "401": $ref: "#/components/responses/Unauthorized" - post: - operationId: createCourseSeries + delete: + operationId: deleteAccountSession tags: - - Course Series - summary: Create a course series + - Account + summary: Delete the current account session description: | - Create a new course series owned by the authenticated user. - - Permission requirements: - - - Only users with the `courseAdmin` role can create course series. - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - title - - courseIDs - - order - properties: - title: - $ref: "#/components/schemas/CourseSeries/properties/title" - thumbnail: - $ref: "#/components/schemas/CourseSeries/properties/thumbnail" - description: - $ref: "#/components/schemas/CourseSeries/properties/description" - courseIDs: - $ref: "#/components/schemas/CourseSeries/properties/courseIDs" - order: - $ref: "#/components/schemas/CourseSeries/properties/order" + Revoke the current account session. Cookie-authenticated mutations must validate Origin or use equivalent CSRF + protection. + security: + - accountSessionCookie: [] responses: - "201": - description: Successfully created the course series. + "204": + description: Account session deleted. headers: - Location: - description: Canonical route of the created course series. + Set-Cookie: + description: Clears the `__Host-xbuilder-account-session` cookie. schema: type: string - format: uri-reference - minLength: 1 - examples: - - /course-series/1 - content: - application/json: - schema: - $ref: "#/components/schemas/CourseSeries" "401": $ref: "#/components/responses/Unauthorized" - "403": - description: Insufficient permissions to perform this operation. - /users: + /account/sessions: get: - operationId: listUsers + operationId: listAccountSessions tags: - - Users - summary: List users - description: Retrieve a paginated list of users. - security: [] + - Account + summary: List current account sessions + description: Retrieve a list of account sessions of the current user. + security: + - accountSessionCookie: [] parameters: - - name: orderBy - description: Field by which to order the results. - in: query - schema: - type: string - enum: - - createdAt - - updatedAt - default: createdAt - examples: - - createdAt - - $ref: "#/components/parameters/SortOrder" - $ref: "#/components/parameters/PageIndex" - $ref: "#/components/parameters/PageSize" responses: @@ -932,48 +655,124 @@ paths: data: type: array items: - $ref: "#/components/schemas/User" + $ref: "#/components/schemas/AccountSession" + "401": + $ref: "#/components/responses/Unauthorized" + delete: + operationId: deleteAccountSessions + tags: + - Account + summary: Delete current account sessions + description: | + Revoke all account sessions of the current user. Cookie-authenticated mutations must validate Origin or use + equivalent CSRF protection. + security: + - accountSessionCookie: [] + responses: + "204": + description: Account sessions deleted. + headers: + Set-Cookie: + description: Clears the `__Host-xbuilder-account-session` cookie. + schema: + type: string + "401": + $ref: "#/components/responses/Unauthorized" - /users/{username}: - get: - operationId: getUser + /account/sessions/{sessionID}: + delete: + operationId: deleteAccountSessionByID tags: - - Users - summary: Get a user - description: Retrieve details of a specific user. - security: [] + - Account + summary: Delete an account session of the current user + description: | + Revoke an account session owned by the current user. Cookie-authenticated mutations must validate Origin or use + equivalent CSRF protection. + security: + - accountSessionCookie: [] parameters: - - name: username - description: Username of the user to get. + - name: sessionID + description: ID of the account session to delete. in: path required: true schema: - $ref: "#/components/schemas/User/properties/username" + $ref: "#/components/schemas/Model/properties/id" + responses: + "204": + description: Account session deleted. + headers: + Set-Cookie: + description: Clears the `__Host-xbuilder-account-session` cookie when the deleted session is current. + schema: + type: string + "401": + $ref: "#/components/responses/Unauthorized" + "404": + description: Account session not found. + + /user: + get: + operationId: getAuthenticatedUser + tags: + - Users + summary: Get the authenticated user + description: Retrieve the profile information of the authenticated user. responses: "200": description: Successful response. content: application/json: schema: - $ref: "#/components/schemas/User" - "301": - $ref: "#/components/responses/MovedPermanently" + $ref: "#/components/schemas/AuthenticatedUser" + "401": + $ref: "#/components/responses/Unauthorized" + patch: + operationId: updateAuthenticatedUser + tags: + - Users + summary: Update the authenticated user + description: Update the profile information of the authenticated user. + requestBody: + required: true + content: + application/json: + schema: + type: object + minProperties: 1 + properties: + username: + description: New username of the authenticated user. If provided, the username will be modified. + $ref: "#/components/schemas/User/properties/username" + displayName: + $ref: "#/components/schemas/User/properties/displayName" + avatar: + description: New avatar Kodo universal URL of the authenticated user. + allOf: + - $ref: "#/components/schemas/User/properties/avatar" + - minLength: 1 + pattern: "^kodo://[^/?#@:]+/[^?#]+$" + examples: + - kodo://xbuilder-usercontent-test/files/FvYyHLNrtx8qFHSwnLjEe57UA2fF-2824936 + description: + $ref: "#/components/schemas/User/properties/description" + responses: + "200": + description: Successfully updated the authenticated user. + content: + application/json: + schema: + $ref: "#/components/schemas/AuthenticatedUser" + "401": + $ref: "#/components/responses/Unauthorized" - /users/{username}/followers: + /user/followers: get: - operationId: listUserFollowers + operationId: listAuthenticatedUserFollowers tags: - Users - summary: List followers of a user - description: Retrieve users who follow the specified user. - security: [] + summary: List followers of the authenticated user + description: Retrieve users who follow the authenticated user. parameters: - - name: username - description: Username of the user whose followers are listed. - in: path - required: true - schema: - $ref: "#/components/schemas/User/properties/username" - name: orderBy description: Field by which to order the results. in: query @@ -1006,24 +805,17 @@ paths: type: array items: $ref: "#/components/schemas/User" - "301": - $ref: "#/components/responses/MovedPermanently" + "401": + $ref: "#/components/responses/Unauthorized" - /users/{username}/following: + /user/following: get: - operationId: listUserFollowing + operationId: listAuthenticatedUserFollowing tags: - Users - summary: List users followed by a user - description: Retrieve users followed by the specified user. - security: [] + summary: List users followed by the authenticated user + description: Retrieve users followed by the authenticated user. parameters: - - name: username - description: Username of the user whose following collection is listed. - in: path - required: true - schema: - $ref: "#/components/schemas/User/properties/username" - name: orderBy description: Field by which to order the results. in: query @@ -1056,29 +848,86 @@ paths: type: array items: $ref: "#/components/schemas/User" - "301": - $ref: "#/components/responses/MovedPermanently" + "401": + $ref: "#/components/responses/Unauthorized" - /users/{username}/projects: + /user/following/{username}: get: - operationId: listUserProjects + operationId: checkAuthenticatedUserFollowing tags: - - Projects - summary: List public projects owned by a user - description: Retrieve public projects owned by the specified user. - security: [] + - Users + summary: Check whether the authenticated user follows a user + description: Determine if the authenticated user follows the specified user. parameters: - name: username - description: Username of the project owner. + description: Username of the user to check. in: path required: true schema: $ref: "#/components/schemas/User/properties/username" - - name: remixedFrom - description: | - Filter remixed projects by the full name of the source project or project release. - - Moved project or project release names are transparently normalized to canonical values. + responses: + "204": + description: The authenticated user follows the specified user. + "301": + $ref: "#/components/responses/MovedPermanently" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + description: The target user does not exist, or the authenticated user does not follow the target user. + put: + operationId: followUser + tags: + - Users + summary: Follow a user + description: Add the specified user to the authenticated user's following collection. + parameters: + - name: username + description: Username of the user to follow. + in: path + required: true + schema: + $ref: "#/components/schemas/User/properties/username" + responses: + "204": + description: Successfully followed the user, or the relationship already existed. + "401": + $ref: "#/components/responses/Unauthorized" + "409": + $ref: "#/components/responses/ResourceMoved" + delete: + operationId: unfollowUser + tags: + - Users + summary: Unfollow a user + description: Remove the specified user from the authenticated user's following collection. + parameters: + - name: username + description: Username of the user to unfollow. + in: path + required: true + schema: + $ref: "#/components/schemas/User/properties/username" + responses: + "204": + description: Successfully unfollowed the user, or the relationship did not exist. + "401": + $ref: "#/components/responses/Unauthorized" + "409": + $ref: "#/components/responses/ResourceMoved" + + /user/projects: + get: + operationId: listAuthenticatedUserProjects + tags: + - Projects + summary: List projects owned by the authenticated user + description: Retrieve a list of projects owned by the authenticated user. + parameters: + - name: remixedFrom + description: | + Filter remixed projects by the full name of the source project or project release. + + Moved project or project release names are transparently normalized to canonical values. in: query schema: oneOf: @@ -1091,6 +940,11 @@ paths: type: string examples: - Niu Xiao Qi + - name: visibility + description: Filter projects by visibility. + in: query + schema: + $ref: "#/components/schemas/Project/properties/visibility" - name: type description: Filter projects by project type. in: query @@ -1149,24 +1003,99 @@ paths: type: array items: $ref: "#/components/schemas/Project" - "301": - $ref: "#/components/responses/MovedPermanently" + "401": + $ref: "#/components/responses/Unauthorized" + post: + operationId: createProject + tags: + - Projects + summary: Create a project + description: | + Create a new project owned by the authenticated user. - /users/{username}/liked-projects: + If `remixSource` is provided, the new project will be a remix of an existing project. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - name + - visibility + properties: + remixSource: + description: | + Full name of the project or project release to remix from. + + Using the project's full name means remixing the latest release sourced from that project. + + Moved project or project release names return `40901` with a `canonical` payload. + oneOf: + - $ref: "#/components/schemas/ProjectFullName" + - $ref: "#/components/schemas/ProjectReleaseFullName" + name: + $ref: "#/components/schemas/Project/properties/name" + displayName: + description: Defaults to `name` if not provided. + $ref: "#/components/schemas/Project/properties/displayName" + type: + description: Defaults to `game` if not provided. + $ref: "#/components/schemas/Project/properties/type" + files: + description: | + File paths and their corresponding universal URLs associated with the project. + + **Required when `remixSource` is not provided.** + $ref: "#/components/schemas/Project/properties/files" + visibility: + $ref: "#/components/schemas/Project/properties/visibility" + description: + $ref: "#/components/schemas/Project/properties/description" + instructions: + $ref: "#/components/schemas/Project/properties/instructions" + extraSettings: + $ref: "#/components/schemas/Project/properties/extraSettings" + thumbnail: + $ref: "#/components/schemas/Project/properties/thumbnail" + responses: + "201": + description: Successfully created the project. + headers: + Location: + description: Canonical route of the created project. + schema: + type: string + format: uri-reference + minLength: 1 + examples: + - /projects/john/NiuXiaoQi + content: + application/json: + schema: + $ref: "#/components/schemas/Project" + "401": + $ref: "#/components/responses/Unauthorized" + "409": + description: | + Returned when `remixSource` references a moved project or project release. + + The response includes a `canonical` payload for the route-derived fields in `remixSource`. + + Clients may update the request and resubmit it explicitly. + content: + application/json: + schema: + $ref: "#/components/schemas/MovedResourceError" + + /user/liked-projects: get: - operationId: listUserLikedProjects + operationId: listAuthenticatedUserLikedProjects tags: - Projects - summary: List projects liked by a user - description: Retrieve public projects liked by the specified user. - security: [] + summary: List projects liked by the authenticated user + description: Retrieve projects liked by the authenticated user. parameters: - - name: username - description: Username of the user whose liked projects are listed. - in: path - required: true - schema: - $ref: "#/components/schemas/User/properties/username" - name: remixedFrom description: | Filter remixed projects by the full name of the source project or project release. @@ -1184,6 +1113,11 @@ paths: type: string examples: - Niu Xiao Qi + - name: visibility + description: Filter projects by visibility. + in: query + schema: + $ref: "#/components/schemas/Project/properties/visibility" - name: type description: Filter projects by project type. in: query @@ -1243,99 +1177,124 @@ paths: type: array items: $ref: "#/components/schemas/Project" - "301": - $ref: "#/components/responses/MovedPermanently" + "401": + $ref: "#/components/responses/Unauthorized" - /users/{username}/assets: + /user/liked-projects/{owner}/{project}: get: - operationId: listUserAssets + operationId: checkAuthenticatedUserLikedProject tags: - - Assets - summary: List public assets owned by a user - description: Retrieve public assets owned by the specified user. - security: [] + - Projects + summary: Check whether the authenticated user liked a project + description: Determine if the authenticated user has liked the specified project. parameters: - - name: username - description: Username of the asset owner. + - name: owner + description: Username of the project's owner. in: path required: true schema: - $ref: "#/components/schemas/User/properties/username" - - name: keyword - description: Filter assets by display name pattern. - in: query - schema: - $ref: "#/components/schemas/Asset/properties/displayName" - - name: type - description: Filter assets by type. - in: query - schema: - $ref: "#/components/schemas/Asset/properties/type" - - name: category - description: Filter assets by category. - in: query - schema: - $ref: "#/components/schemas/Asset/properties/category" - - name: filesHash - description: Filter assets by files hash. - in: query - schema: - $ref: "#/components/schemas/Asset/properties/filesHash" - - name: orderBy - description: Field by which to order the results. - in: query + $ref: "#/components/schemas/Project/properties/owner" + - name: project + description: Name of the project to check. + in: path + required: true schema: - type: string - enum: - - createdAt - - updatedAt - - displayName - default: createdAt - examples: - - createdAt - - $ref: "#/components/parameters/SortOrder" - - $ref: "#/components/parameters/PageIndex" - - $ref: "#/components/parameters/PageSize" + $ref: "#/components/schemas/Project/properties/name" responses: - "200": - description: Successful response. - content: - application/json: - schema: - type: object - required: - - total - - data - properties: - total: - $ref: "#/components/schemas/ByPage/properties/total" - data: - type: array - items: - $ref: "#/components/schemas/Asset" + "204": + description: The project is liked by the authenticated user. "301": $ref: "#/components/responses/MovedPermanently" - - /users/{username}/courses: - get: - operationId: listUserCourses - tags: - - Courses - summary: List courses owned by a user - description: Retrieve courses owned by the specified user. - security: [] + "401": + $ref: "#/components/responses/Unauthorized" + "404": + description: The project does not exist, or the authenticated user has not liked the project. + put: + operationId: likeProject + tags: + - Projects + summary: Like a project + description: Add the project to the authenticated user's liked-projects collection. parameters: - - name: username - description: Username of the course owner. + - name: owner + description: Username of the project's owner. in: path required: true schema: - $ref: "#/components/schemas/User/properties/username" - - name: courseSeriesID - description: Filter courses by the course series ID. + $ref: "#/components/schemas/Project/properties/owner" + - name: project + description: Name of the project to like. + in: path + required: true + schema: + $ref: "#/components/schemas/Project/properties/name" + responses: + "204": + description: Successfully liked the project, or the relationship already existed. + "401": + $ref: "#/components/responses/Unauthorized" + "409": + $ref: "#/components/responses/ResourceMoved" + delete: + operationId: unlikeProject + tags: + - Projects + summary: Unlike a project + description: Remove the project from the authenticated user's liked-projects collection. + parameters: + - name: owner + description: Username of the project's owner. + in: path + required: true + schema: + $ref: "#/components/schemas/Project/properties/owner" + - name: project + description: Name of the project to unlike. + in: path + required: true + schema: + $ref: "#/components/schemas/Project/properties/name" + responses: + "204": + description: Successfully unliked the project, or the relationship did not exist. + "401": + $ref: "#/components/responses/Unauthorized" + "409": + $ref: "#/components/responses/ResourceMoved" + + /user/assets: + get: + operationId: listAuthenticatedUserAssets + tags: + - Assets + summary: List assets owned by the authenticated user + description: Retrieve a list of assets owned by the authenticated user. + parameters: + - name: keyword + description: Filter assets by display name pattern. in: query schema: - $ref: "#/components/schemas/Model/properties/id" + $ref: "#/components/schemas/Asset/properties/displayName" + - name: type + description: Filter assets by type. + in: query + schema: + $ref: "#/components/schemas/Asset/properties/type" + - name: category + description: Filter assets by category. + in: query + schema: + $ref: "#/components/schemas/Asset/properties/category" + - name: filesHash + description: Filter assets by files hash. + in: query + schema: + $ref: "#/components/schemas/Asset/properties/filesHash" + - name: visibility + description: Filter assets by visibility. + in: query + schema: + $ref: "#/components/schemas/Asset/properties/visibility" - name: orderBy description: Field by which to order the results. in: query @@ -1344,7 +1303,7 @@ paths: enum: - createdAt - updatedAt - - sequenceInCourseSeries + - displayName default: createdAt examples: - createdAt @@ -1367,25 +1326,87 @@ paths: data: type: array items: - $ref: "#/components/schemas/Course" - "301": - $ref: "#/components/responses/MovedPermanently" + $ref: "#/components/schemas/Asset" + "401": + $ref: "#/components/responses/Unauthorized" + post: + operationId: createAsset + tags: + - Assets + summary: Create an asset + description: | + Create a new asset owned by the authenticated user. - /users/{username}/course-series: + Permission requirements: + + - Only users with the `assetAdmin` role can create public assets. + - Regular users can only create private assets. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - displayName + - type + - category + - description + - extraSettings + - files + - filesHash + - visibility + properties: + displayName: + $ref: "#/components/schemas/Asset/properties/displayName" + type: + $ref: "#/components/schemas/Asset/properties/type" + category: + $ref: "#/components/schemas/Asset/properties/category" + description: + $ref: "#/components/schemas/Asset/properties/description" + extraSettings: + $ref: "#/components/schemas/Asset/properties/extraSettings" + files: + $ref: "#/components/schemas/Asset/properties/files" + filesHash: + $ref: "#/components/schemas/Asset/properties/filesHash" + visibility: + $ref: "#/components/schemas/Asset/properties/visibility" + responses: + "201": + description: Successfully created the asset. + headers: + Location: + description: Canonical route of the created asset. + schema: + type: string + format: uri-reference + minLength: 1 + examples: + - /assets/1 + content: + application/json: + schema: + $ref: "#/components/schemas/Asset" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. + + /user/courses: get: - operationId: listUserCourseSeries + operationId: listAuthenticatedUserCourses tags: - - Course Series - summary: List course series owned by a user - description: Retrieve course series owned by the specified user. - security: [] + - Courses + summary: List courses owned by the authenticated user + description: Retrieve courses owned by the authenticated user. parameters: - - name: username - description: Username of the course series owner. - in: path - required: true + - name: courseSeriesID + description: Filter courses by the course series ID. + in: query schema: - $ref: "#/components/schemas/User/properties/username" + $ref: "#/components/schemas/Model/properties/id" - name: orderBy description: Field by which to order the results. in: query @@ -1394,10 +1415,10 @@ paths: enum: - createdAt - updatedAt - - order - default: order + - sequenceInCourseSeries + default: createdAt examples: - - order + - createdAt - $ref: "#/components/parameters/SortOrder" - $ref: "#/components/parameters/PageIndex" - $ref: "#/components/parameters/PageSize" @@ -1417,80 +1438,72 @@ paths: data: type: array items: - $ref: "#/components/schemas/CourseSeries" - "301": - $ref: "#/components/responses/MovedPermanently" - - /projects: - get: - operationId: listProjects + $ref: "#/components/schemas/Course" + "401": + $ref: "#/components/responses/Unauthorized" + post: + operationId: createCourse tags: - - Projects - summary: List projects + - Courses + summary: Create a course description: | - Retrieve a paginated list of projects visible to the request. + Create a new course owned by the authenticated user. - Anonymous requests return public projects. Authenticated requests return public projects and the authenticated - user's own projects when no visibility filter is provided. A non-public visibility filter requires - authentication and is scoped to the authenticated user's own projects. - security: - - {} - - bearerAuth: [] - parameters: - - name: remixedFrom - description: | - Filter remixed projects by the full name of the source project or project release. + Permission requirements: - Moved project or project release names are transparently normalized to canonical values. - in: query - schema: - oneOf: - - $ref: "#/components/schemas/ProjectFullName" - - $ref: "#/components/schemas/ProjectReleaseFullName" - - name: keyword - description: Filter projects by display name or name pattern. - in: query - schema: - type: string - examples: - - Niu Xiao Qi - - name: visibility - description: Filter projects by visibility. - in: query - schema: - $ref: "#/components/schemas/Project/properties/visibility" - - name: type - description: Filter projects by project type. - in: query - schema: - $ref: "#/components/schemas/Project/properties/type" - - name: createdAfter - description: Filter projects created after this timestamp. - in: query - schema: - type: string - format: date-time - - name: likesReceivedAfter - description: Filter projects that gained new likes after this timestamp. - in: query - schema: - type: string - format: date-time - - name: remixesReceivedAfter - description: Filter projects that were remixed after this timestamp. - in: query - schema: - type: string - format: date-time - - name: fromFollowees - description: | - When true, filter to projects created by the authenticated user's followees. + - Only users with the `courseAdmin` role can create courses. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - title + - thumbnail + - entrypoint + - references + - prompt + properties: + title: + $ref: "#/components/schemas/Course/properties/title" + thumbnail: + $ref: "#/components/schemas/Course/properties/thumbnail" + entrypoint: + $ref: "#/components/schemas/Course/properties/entrypoint" + references: + $ref: "#/components/schemas/Course/properties/references" + prompt: + $ref: "#/components/schemas/Course/properties/prompt" + responses: + "201": + description: Successfully created the course. + headers: + Location: + description: Canonical route of the created course. + schema: + type: string + format: uri-reference + minLength: 1 + examples: + - /courses/1 + content: + application/json: + schema: + $ref: "#/components/schemas/Course" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. - Supplying `true` without authentication returns `401 Unauthorized`. - in: query - schema: - type: boolean - default: false + /user/course-series: + get: + operationId: listAuthenticatedUserCourseSeries + tags: + - Course Series + summary: List course series owned by the authenticated user + description: Retrieve course series owned by the authenticated user. + parameters: - name: orderBy description: Field by which to order the results. in: query @@ -1499,13 +1512,10 @@ paths: enum: - createdAt - updatedAt - - likeCount - - remixCount - - recentLikeCount - - recentRemixCount - default: createdAt + - order + default: order examples: - - createdAt + - order - $ref: "#/components/parameters/SortOrder" - $ref: "#/components/parameters/PageIndex" - $ref: "#/components/parameters/PageSize" @@ -1525,177 +1535,143 @@ paths: data: type: array items: - $ref: "#/components/schemas/Project" + $ref: "#/components/schemas/CourseSeries" "401": $ref: "#/components/responses/Unauthorized" + post: + operationId: createCourseSeries + tags: + - Course Series + summary: Create a course series + description: | + Create a new course series owned by the authenticated user. - /projects/{owner}/{project}: + Permission requirements: + + - Only users with the `courseAdmin` role can create course series. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - title + - courseIDs + - order + properties: + title: + $ref: "#/components/schemas/CourseSeries/properties/title" + thumbnail: + $ref: "#/components/schemas/CourseSeries/properties/thumbnail" + description: + $ref: "#/components/schemas/CourseSeries/properties/description" + courseIDs: + $ref: "#/components/schemas/CourseSeries/properties/courseIDs" + order: + $ref: "#/components/schemas/CourseSeries/properties/order" + responses: + "201": + description: Successfully created the course series. + headers: + Location: + description: Canonical route of the created course series. + schema: + type: string + format: uri-reference + minLength: 1 + examples: + - /course-series/1 + content: + application/json: + schema: + $ref: "#/components/schemas/CourseSeries" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. + + /users: get: - operationId: getProject + operationId: listUsers tags: - - Projects - summary: Get a project - description: Retrieve details of a specific project. - security: - - {} - - bearerAuth: [] + - Users + summary: List users + description: Retrieve a list of users. + security: [] parameters: - - name: owner - description: Username of the project's owner. - in: path - required: true - schema: - $ref: "#/components/schemas/Project/properties/owner" - - name: project - description: Name of the project to get. - in: path - required: true + - name: orderBy + description: Field by which to order the results. + in: query schema: - $ref: "#/components/schemas/Project/properties/name" + type: string + enum: + - createdAt + - updatedAt + default: createdAt + examples: + - createdAt + - $ref: "#/components/parameters/SortOrder" + - $ref: "#/components/parameters/PageIndex" + - $ref: "#/components/parameters/PageSize" responses: "200": description: Successful response. content: application/json: schema: - $ref: "#/components/schemas/Project" - "301": - $ref: "#/components/responses/MovedPermanently" - "401": - $ref: "#/components/responses/Unauthorized" - patch: - operationId: updateProject + type: object + required: + - total + - data + properties: + total: + $ref: "#/components/schemas/ByPage/properties/total" + data: + type: array + items: + $ref: "#/components/schemas/User" + + /users/{username}: + get: + operationId: getUser tags: - - Projects - summary: Update a project - description: Update the details of a specific project. + - Users + summary: Get a user + description: Retrieve details of a specific user. + security: [] parameters: - - name: owner - description: Username of the project's owner. - in: path - required: true - schema: - $ref: "#/components/schemas/Project/properties/owner" - - name: project - description: Name of the project to update. + - name: username + description: Username of the user to get. in: path required: true schema: - $ref: "#/components/schemas/Project/properties/name" - requestBody: - required: true - content: - application/json: - schema: - type: object - minProperties: 1 - properties: - name: - description: New name of the project. If provided, the project will be renamed. - $ref: "#/components/schemas/Project/properties/name" - files: - $ref: "#/components/schemas/Project/properties/files" - visibility: - $ref: "#/components/schemas/Project/properties/visibility" - displayName: - $ref: "#/components/schemas/Project/properties/displayName" - description: - $ref: "#/components/schemas/Project/properties/description" - instructions: - $ref: "#/components/schemas/Project/properties/instructions" - extraSettings: - $ref: "#/components/schemas/Project/properties/extraSettings" - thumbnail: - $ref: "#/components/schemas/Project/properties/thumbnail" + $ref: "#/components/schemas/User/properties/username" responses: "200": - description: Successfully updated the project. + description: Successful response. content: application/json: schema: - $ref: "#/components/schemas/Project" - "401": - $ref: "#/components/responses/Unauthorized" - "409": - $ref: "#/components/responses/ResourceMoved" - delete: - operationId: deleteProject + $ref: "#/components/schemas/User" + "301": + $ref: "#/components/responses/MovedPermanently" + + /users/{username}/followers: + get: + operationId: listUserFollowers tags: - - Projects - summary: Delete a project - description: Permanently delete a specific project. + - Users + summary: List followers of a user + description: Retrieve users who follow the specified user. + security: [] parameters: - - name: owner - description: Username of the project's owner. + - name: username + description: Username of the user whose followers are listed. in: path required: true schema: - $ref: "#/components/schemas/Project/properties/owner" - - name: project - description: Name of the project to delete. - in: path - required: true - schema: - $ref: "#/components/schemas/Project/properties/name" - responses: - "204": - description: Successfully deleted the project. - "401": - $ref: "#/components/responses/Unauthorized" - "409": - $ref: "#/components/responses/ResourceMoved" - - /projects/{owner}/{project}/views: - post: - operationId: recordProjectView - tags: - - Projects - summary: Record a project view - description: Record a view for the specified project as the authenticated user. - parameters: - - name: owner - description: Username of the project's owner. - in: path - required: true - schema: - $ref: "#/components/schemas/Project/properties/owner" - - name: project - description: Name of the project to record a view for. - in: path - required: true - schema: - $ref: "#/components/schemas/Project/properties/name" - responses: - "204": - description: Successfully recorded the project view. - "401": - $ref: "#/components/responses/Unauthorized" - "409": - $ref: "#/components/responses/ResourceMoved" - - /projects/{owner}/{project}/releases: - get: - operationId: listProjectReleases - tags: - - Project Releases - summary: List project releases - description: Retrieve a paginated list of releases for a project. - security: - - {} - - bearerAuth: [] - parameters: - - name: owner - description: Username of the project's owner. - in: path - required: true - schema: - $ref: "#/components/schemas/Project/properties/owner" - - name: project - description: Name of the project. - in: path - required: true - schema: - $ref: "#/components/schemas/Project/properties/name" + $ref: "#/components/schemas/User/properties/username" - name: orderBy description: Field by which to order the results. in: query @@ -1704,10 +1680,10 @@ paths: enum: - createdAt - updatedAt - - remixCount - default: createdAt + - followedAt + default: followedAt examples: - - createdAt + - followedAt - $ref: "#/components/parameters/SortOrder" - $ref: "#/components/parameters/PageIndex" - $ref: "#/components/parameters/PageSize" @@ -1727,158 +1703,25 @@ paths: data: type: array items: - $ref: "#/components/schemas/ProjectRelease" + $ref: "#/components/schemas/User" "301": $ref: "#/components/responses/MovedPermanently" - "401": - $ref: "#/components/responses/Unauthorized" - post: - operationId: createProjectRelease - tags: - - Project Releases - summary: Create a project release - description: Create a new release for a specific project. - parameters: - - name: owner - description: Username of the project's owner. - in: path - required: true - schema: - $ref: "#/components/schemas/Project/properties/owner" - - name: project - description: Name of the project. - in: path - required: true - schema: - $ref: "#/components/schemas/Project/properties/name" - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - name - - description - properties: - name: - $ref: "#/components/schemas/ProjectRelease/properties/name" - description: - $ref: "#/components/schemas/ProjectRelease/properties/description" - thumbnail: - $ref: "#/components/schemas/ProjectRelease/properties/thumbnail" - responses: - "201": - description: Successfully created the project release. - headers: - Location: - description: Canonical route of the created project release. - schema: - type: string - format: uri-reference - minLength: 1 - examples: - - /projects/john/NiuXiaoQi/releases/v1.2.3 - content: - application/json: - schema: - $ref: "#/components/schemas/ProjectRelease" - "401": - $ref: "#/components/responses/Unauthorized" - "409": - description: | - Returned when the project path references a moved project. - - The response includes a `canonical` payload for the project route. - - Clients may update the request and resubmit it explicitly. - content: - application/json: - schema: - $ref: "#/components/schemas/MovedResourceError" - /projects/{owner}/{project}/releases/{release}: + /users/{username}/following: get: - operationId: getProjectRelease + operationId: listUserFollowing tags: - - Project Releases - summary: Get a project release - description: Retrieve details of a specific project release. - security: - - {} - - bearerAuth: [] + - Users + summary: List users followed by a user + description: Retrieve users followed by the specified user. + security: [] parameters: - - name: owner - description: Username of the project's owner. - in: path - required: true - schema: - $ref: "#/components/schemas/Project/properties/owner" - - name: project - description: Name of the project. - in: path - required: true - schema: - $ref: "#/components/schemas/Project/properties/name" - - name: release - description: Name of the project release to get. + - name: username + description: Username of the user whose following collection is listed. in: path required: true schema: - $ref: "#/components/schemas/ProjectRelease/properties/name" - responses: - "200": - description: Successful response. - content: - application/json: - schema: - $ref: "#/components/schemas/ProjectRelease" - "301": - $ref: "#/components/responses/MovedPermanently" - "401": - $ref: "#/components/responses/Unauthorized" - - /assets: - get: - operationId: listAssets - tags: - - Assets - summary: List assets - description: | - Retrieve a paginated list of assets visible to the request. - - Anonymous requests return public assets. Authenticated requests return public assets and the authenticated - user's own assets when no visibility filter is provided. A non-public visibility filter requires authentication - and is scoped to the authenticated user's own assets. - security: - - {} - - bearerAuth: [] - parameters: - - name: keyword - description: Filter assets by display name pattern. - in: query - schema: - $ref: "#/components/schemas/Asset/properties/displayName" - - name: type - description: Filter assets by type. - in: query - schema: - $ref: "#/components/schemas/Asset/properties/type" - - name: category - description: Filter assets by category. - in: query - schema: - $ref: "#/components/schemas/Asset/properties/category" - - name: filesHash - description: Filter assets by files hash. - in: query - schema: - $ref: "#/components/schemas/Asset/properties/filesHash" - - name: visibility - description: Filter assets by visibility. - in: query - schema: - $ref: "#/components/schemas/Asset/properties/visibility" + $ref: "#/components/schemas/User/properties/username" - name: orderBy description: Field by which to order the results. in: query @@ -1887,10 +1730,10 @@ paths: enum: - createdAt - updatedAt - - displayName - default: createdAt + - followedAt + default: followedAt examples: - - createdAt + - followedAt - $ref: "#/components/parameters/SortOrder" - $ref: "#/components/parameters/PageIndex" - $ref: "#/components/parameters/PageSize" @@ -1910,125 +1753,65 @@ paths: data: type: array items: - $ref: "#/components/schemas/Asset" - "401": - $ref: "#/components/responses/Unauthorized" + $ref: "#/components/schemas/User" + "301": + $ref: "#/components/responses/MovedPermanently" - /assets/{assetID}: + /users/{username}/projects: get: - operationId: getAsset + operationId: listUserProjects tags: - - Assets - summary: Get an asset - description: Retrieve details of a specific asset. - security: - - {} - - bearerAuth: [] + - Projects + summary: List public projects owned by a user + description: Retrieve public projects owned by the specified user. + security: [] parameters: - - name: assetID - description: ID of the asset to get. + - name: username + description: Username of the project owner. in: path required: true schema: - $ref: "#/components/schemas/Model/properties/id" - responses: - "200": - description: Successful response. - content: - application/json: - schema: - $ref: "#/components/schemas/Asset" - "401": - $ref: "#/components/responses/Unauthorized" - patch: - operationId: updateAsset - tags: - - Assets - summary: Update an asset - description: | - Update the details of a specific asset. - - Permission requirements: + $ref: "#/components/schemas/User/properties/username" + - name: remixedFrom + description: | + Filter remixed projects by the full name of the source project or project release. - - Only users with the `assetAdmin` role can update assets to public visibility. - - Asset owners can update their private assets. - parameters: - - name: assetID - description: ID of the asset to update. - in: path - required: true + Moved project or project release names are transparently normalized to canonical values. + in: query schema: - $ref: "#/components/schemas/Model/properties/id" - requestBody: - required: true - content: - application/json: - schema: - type: object - minProperties: 1 - properties: - displayName: - $ref: "#/components/schemas/Asset/properties/displayName" - type: - $ref: "#/components/schemas/Asset/properties/type" - category: - $ref: "#/components/schemas/Asset/properties/category" - description: - $ref: "#/components/schemas/Asset/properties/description" - extraSettings: - $ref: "#/components/schemas/Asset/properties/extraSettings" - files: - $ref: "#/components/schemas/Asset/properties/files" - filesHash: - $ref: "#/components/schemas/Asset/properties/filesHash" - visibility: - $ref: "#/components/schemas/Asset/properties/visibility" - responses: - "200": - description: Successfully updated the asset. - content: - application/json: - schema: - $ref: "#/components/schemas/Asset" - "401": - $ref: "#/components/responses/Unauthorized" - "403": - description: Insufficient permissions to perform this operation. - delete: - operationId: deleteAsset - tags: - - Assets - summary: Delete an asset - description: Permanently delete a specific asset. - parameters: - - name: assetID - description: ID of the asset to delete. - in: path - required: true + oneOf: + - $ref: "#/components/schemas/ProjectFullName" + - $ref: "#/components/schemas/ProjectReleaseFullName" + - name: keyword + description: Filter projects by display name or name pattern. + in: query schema: - $ref: "#/components/schemas/Model/properties/id" - responses: - "204": - description: Successfully deleted the asset. - "401": - $ref: "#/components/responses/Unauthorized" - - /courses: - get: - operationId: listCourses - tags: - - Courses - summary: List courses - description: Retrieve a paginated list of courses visible to the request. - security: - - {} - - bearerAuth: [] - parameters: - - name: courseSeriesID - description: Filter courses by the course series ID. + type: string + examples: + - Niu Xiao Qi + - name: type + description: Filter projects by project type. in: query schema: - $ref: "#/components/schemas/Model/properties/id" + $ref: "#/components/schemas/Project/properties/type" + - name: createdAfter + description: Filter projects created after this timestamp. + in: query + schema: + type: string + format: date-time + - name: likesReceivedAfter + description: Filter projects that gained new likes after this timestamp. + in: query + schema: + type: string + format: date-time + - name: remixesReceivedAfter + description: Filter projects that were remixed after this timestamp. + in: query + schema: + type: string + format: date-time - name: orderBy description: Field by which to order the results. in: query @@ -2037,7 +1820,10 @@ paths: enum: - createdAt - updatedAt - - sequenceInCourseSeries + - likeCount + - remixCount + - recentLikeCount + - recentRemixCount default: createdAt examples: - createdAt @@ -2060,116 +1846,244 @@ paths: data: type: array items: - $ref: "#/components/schemas/Course" + $ref: "#/components/schemas/Project" + "301": + $ref: "#/components/responses/MovedPermanently" - /courses/{courseID}: + /users/{username}/liked-projects: get: - operationId: getCourse + operationId: listUserLikedProjects tags: - - Courses - summary: Get a course - description: Retrieve details of a specific course. - security: - - {} - - bearerAuth: [] + - Projects + summary: List projects liked by a user + description: Retrieve public projects liked by the specified user. + security: [] parameters: - - name: courseID - description: ID of the course to get. + - name: username + description: Username of the user whose liked projects are listed. in: path required: true schema: - $ref: "#/components/schemas/Model/properties/id" + $ref: "#/components/schemas/User/properties/username" + - name: remixedFrom + description: | + Filter remixed projects by the full name of the source project or project release. + + Moved project or project release names are transparently normalized to canonical values. + in: query + schema: + oneOf: + - $ref: "#/components/schemas/ProjectFullName" + - $ref: "#/components/schemas/ProjectReleaseFullName" + - name: keyword + description: Filter projects by display name or name pattern. + in: query + schema: + type: string + examples: + - Niu Xiao Qi + - name: type + description: Filter projects by project type. + in: query + schema: + $ref: "#/components/schemas/Project/properties/type" + - name: createdAfter + description: Filter projects created after this timestamp. + in: query + schema: + type: string + format: date-time + - name: likesReceivedAfter + description: Filter projects that gained new likes after this timestamp. + in: query + schema: + type: string + format: date-time + - name: remixesReceivedAfter + description: Filter projects that were remixed after this timestamp. + in: query + schema: + type: string + format: date-time + - name: orderBy + description: Field by which to order the results. + in: query + schema: + type: string + enum: + - createdAt + - updatedAt + - likeCount + - remixCount + - recentLikeCount + - recentRemixCount + - likedAt + default: likedAt + examples: + - likedAt + - $ref: "#/components/parameters/SortOrder" + - $ref: "#/components/parameters/PageIndex" + - $ref: "#/components/parameters/PageSize" responses: "200": description: Successful response. content: application/json: schema: - $ref: "#/components/schemas/Course" - patch: - operationId: updateCourse - tags: - - Courses - summary: Update a course - description: | - Update the details of a specific course. - - Permission requirements: + type: object + required: + - total + - data + properties: + total: + $ref: "#/components/schemas/ByPage/properties/total" + data: + type: array + items: + $ref: "#/components/schemas/Project" + "301": + $ref: "#/components/responses/MovedPermanently" - - Only users with the `courseAdmin` role can update courses. + /users/{username}/assets: + get: + operationId: listUserAssets + tags: + - Assets + summary: List public assets owned by a user + description: Retrieve public assets owned by the specified user. + security: [] parameters: - - name: courseID - description: ID of the course to update. + - name: username + description: Username of the asset owner. in: path required: true schema: - $ref: "#/components/schemas/Model/properties/id" - requestBody: - required: true - content: - application/json: - schema: - type: object - minProperties: 1 - properties: - title: - $ref: "#/components/schemas/Course/properties/title" - thumbnail: - $ref: "#/components/schemas/Course/properties/thumbnail" - entrypoint: - $ref: "#/components/schemas/Course/properties/entrypoint" - references: - $ref: "#/components/schemas/Course/properties/references" - prompt: - $ref: "#/components/schemas/Course/properties/prompt" + $ref: "#/components/schemas/User/properties/username" + - name: keyword + description: Filter assets by display name pattern. + in: query + schema: + $ref: "#/components/schemas/Asset/properties/displayName" + - name: type + description: Filter assets by type. + in: query + schema: + $ref: "#/components/schemas/Asset/properties/type" + - name: category + description: Filter assets by category. + in: query + schema: + $ref: "#/components/schemas/Asset/properties/category" + - name: filesHash + description: Filter assets by files hash. + in: query + schema: + $ref: "#/components/schemas/Asset/properties/filesHash" + - name: orderBy + description: Field by which to order the results. + in: query + schema: + type: string + enum: + - createdAt + - updatedAt + - displayName + default: createdAt + examples: + - createdAt + - $ref: "#/components/parameters/SortOrder" + - $ref: "#/components/parameters/PageIndex" + - $ref: "#/components/parameters/PageSize" responses: "200": - description: Successfully updated the course. + description: Successful response. content: application/json: schema: - $ref: "#/components/schemas/Course" - "401": - $ref: "#/components/responses/Unauthorized" - "403": - description: Insufficient permissions to perform this operation. - delete: - operationId: deleteCourse + type: object + required: + - total + - data + properties: + total: + $ref: "#/components/schemas/ByPage/properties/total" + data: + type: array + items: + $ref: "#/components/schemas/Asset" + "301": + $ref: "#/components/responses/MovedPermanently" + + /users/{username}/courses: + get: + operationId: listUserCourses tags: - Courses - summary: Delete a course - description: | - Permanently delete a specific course. - - Permission requirements: - - - Only users with the `courseAdmin` role can delete courses. + summary: List courses owned by a user + description: Retrieve courses owned by the specified user. + security: [] parameters: - - name: courseID - description: ID of the course to delete. + - name: username + description: Username of the course owner. in: path required: true + schema: + $ref: "#/components/schemas/User/properties/username" + - name: courseSeriesID + description: Filter courses by the course series ID. + in: query schema: $ref: "#/components/schemas/Model/properties/id" + - name: orderBy + description: Field by which to order the results. + in: query + schema: + type: string + enum: + - createdAt + - updatedAt + - sequenceInCourseSeries + default: createdAt + examples: + - createdAt + - $ref: "#/components/parameters/SortOrder" + - $ref: "#/components/parameters/PageIndex" + - $ref: "#/components/parameters/PageSize" responses: - "204": - description: Successfully deleted the course. - "401": - $ref: "#/components/responses/Unauthorized" - "403": - description: Insufficient permissions to perform this operation. + "200": + description: Successful response. + content: + application/json: + schema: + type: object + required: + - total + - data + properties: + total: + $ref: "#/components/schemas/ByPage/properties/total" + data: + type: array + items: + $ref: "#/components/schemas/Course" + "301": + $ref: "#/components/responses/MovedPermanently" - /course-series: + /users/{username}/course-series: get: - operationId: listCourseSeries + operationId: listUserCourseSeries tags: - Course Series - summary: List course series - description: Retrieve a paginated list of course series visible to the request. - security: - - {} - - bearerAuth: [] + summary: List course series owned by a user + description: Retrieve course series owned by the specified user. + security: [] parameters: + - name: username + description: Username of the course series owner. + in: path + required: true + schema: + $ref: "#/components/schemas/User/properties/username" - name: orderBy description: Field by which to order the results. in: query @@ -2202,49 +2116,170 @@ paths: type: array items: $ref: "#/components/schemas/CourseSeries" + "301": + $ref: "#/components/responses/MovedPermanently" - /course-series/{courseSeriesID}: + /projects: get: - operationId: getCourseSeries + operationId: listProjects tags: - - Course Series - summary: Get a course series - description: Retrieve details of a specific course series. + - Projects + summary: List projects + description: | + Retrieve a list of projects visible to the request. + + Anonymous requests return public projects. Authenticated requests return public projects and the authenticated + user's own projects when no visibility filter is provided. A non-public visibility filter requires + authentication and is scoped to the authenticated user's own projects. security: - {} - bearerAuth: [] parameters: - - name: courseSeriesID - description: ID of the course series to get. - in: path - required: true + - name: remixedFrom + description: | + Filter remixed projects by the full name of the source project or project release. + + Moved project or project release names are transparently normalized to canonical values. + in: query schema: - $ref: "#/components/schemas/Model/properties/id" + oneOf: + - $ref: "#/components/schemas/ProjectFullName" + - $ref: "#/components/schemas/ProjectReleaseFullName" + - name: keyword + description: Filter projects by display name or name pattern. + in: query + schema: + type: string + examples: + - Niu Xiao Qi + - name: visibility + description: Filter projects by visibility. + in: query + schema: + $ref: "#/components/schemas/Project/properties/visibility" + - name: type + description: Filter projects by project type. + in: query + schema: + $ref: "#/components/schemas/Project/properties/type" + - name: createdAfter + description: Filter projects created after this timestamp. + in: query + schema: + type: string + format: date-time + - name: likesReceivedAfter + description: Filter projects that gained new likes after this timestamp. + in: query + schema: + type: string + format: date-time + - name: remixesReceivedAfter + description: Filter projects that were remixed after this timestamp. + in: query + schema: + type: string + format: date-time + - name: fromFollowees + description: | + When true, filter to projects created by the authenticated user's followees. + + Supplying `true` without authentication returns `401 Unauthorized`. + in: query + schema: + type: boolean + default: false + - name: orderBy + description: Field by which to order the results. + in: query + schema: + type: string + enum: + - createdAt + - updatedAt + - likeCount + - remixCount + - recentLikeCount + - recentRemixCount + default: createdAt + examples: + - createdAt + - $ref: "#/components/parameters/SortOrder" + - $ref: "#/components/parameters/PageIndex" + - $ref: "#/components/parameters/PageSize" responses: "200": description: Successful response. content: application/json: schema: - $ref: "#/components/schemas/CourseSeries" - patch: - operationId: updateCourseSeries - tags: - - Course Series - summary: Update a course series - description: | - Update the details of a specific course series. - - Permission requirements: - - - Only users with the `courseAdmin` role can update course series. - parameters: - - name: courseSeriesID - description: ID of the course series to update. + type: object + required: + - total + - data + properties: + total: + $ref: "#/components/schemas/ByPage/properties/total" + data: + type: array + items: + $ref: "#/components/schemas/Project" + "401": + $ref: "#/components/responses/Unauthorized" + + /projects/{owner}/{project}: + get: + operationId: getProject + tags: + - Projects + summary: Get a project + description: Retrieve details of a specific project. + security: + - {} + - bearerAuth: [] + parameters: + - name: owner + description: Username of the project's owner. in: path required: true schema: - $ref: "#/components/schemas/Model/properties/id" + $ref: "#/components/schemas/Project/properties/owner" + - name: project + description: Name of the project to get. + in: path + required: true + schema: + $ref: "#/components/schemas/Project/properties/name" + responses: + "200": + description: Successful response. + content: + application/json: + schema: + $ref: "#/components/schemas/Project" + "301": + $ref: "#/components/responses/MovedPermanently" + "401": + $ref: "#/components/responses/Unauthorized" + patch: + operationId: updateProject + tags: + - Projects + summary: Update a project + description: Update the details of a specific project. + parameters: + - name: owner + description: Username of the project's owner. + in: path + required: true + schema: + $ref: "#/components/schemas/Project/properties/owner" + - name: project + description: Name of the project to update. + in: path + required: true + schema: + $ref: "#/components/schemas/Project/properties/name" requestBody: required: true content: @@ -2253,351 +2288,1943 @@ paths: type: object minProperties: 1 properties: - title: - $ref: "#/components/schemas/CourseSeries/properties/title" - thumbnail: - $ref: "#/components/schemas/CourseSeries/properties/thumbnail" + name: + description: New name of the project. If provided, the project will be renamed. + $ref: "#/components/schemas/Project/properties/name" + files: + $ref: "#/components/schemas/Project/properties/files" + visibility: + $ref: "#/components/schemas/Project/properties/visibility" + displayName: + $ref: "#/components/schemas/Project/properties/displayName" description: - $ref: "#/components/schemas/CourseSeries/properties/description" - courseIDs: - $ref: "#/components/schemas/CourseSeries/properties/courseIDs" - order: - $ref: "#/components/schemas/CourseSeries/properties/order" + $ref: "#/components/schemas/Project/properties/description" + instructions: + $ref: "#/components/schemas/Project/properties/instructions" + extraSettings: + $ref: "#/components/schemas/Project/properties/extraSettings" + thumbnail: + $ref: "#/components/schemas/Project/properties/thumbnail" responses: "200": - description: Successfully updated the course series. + description: Successfully updated the project. content: application/json: schema: - $ref: "#/components/schemas/CourseSeries" + $ref: "#/components/schemas/Project" "401": $ref: "#/components/responses/Unauthorized" - "403": - description: Insufficient permissions to perform this operation. + "409": + $ref: "#/components/responses/ResourceMoved" delete: - operationId: deleteCourseSeries + operationId: deleteProject tags: - - Course Series - summary: Delete a course series - description: | - Permanently delete a specific course series. - - Permission requirements: - - - Only users with the `courseAdmin` role can delete course series. + - Projects + summary: Delete a project + description: Permanently delete a specific project. parameters: - - name: courseSeriesID - description: ID of the course series to delete. + - name: owner + description: Username of the project's owner. in: path required: true schema: - $ref: "#/components/schemas/Model/properties/id" + $ref: "#/components/schemas/Project/properties/owner" + - name: project + description: Name of the project to delete. + in: path + required: true + schema: + $ref: "#/components/schemas/Project/properties/name" responses: "204": - description: Successfully deleted the course series. + description: Successfully deleted the project. "401": $ref: "#/components/responses/Unauthorized" - "403": - description: Insufficient permissions to perform this operation. + "409": + $ref: "#/components/responses/ResourceMoved" - /copilot/messages: + /projects/{owner}/{project}/views: post: - operationId: createCopilotMessage + operationId: recordProjectView tags: - - Copilot - summary: Generate a structured SSE stream message - description: | - Generate a structured SSE stream message by sending a list of input messages. - - The response is a Server-Sent Events stream where each `data:` line contains a single JSON object. - - Event semantics: - - | Event | Data schema | Semantics | - |-------|-------------|-----------| - | `text_delta` | `CopilotSSETextDeltaEvent` | Incremental assistant text fragment. Concatenate `text` values in order to reconstruct `content.text`. | - | `tool_call_delta` | `CopilotSSEToolCallDeltaEvent` | Incremental fragment for one assistant tool call. Merge fragments by `index`; `id`, `function.name`, and `function.arguments` are omitted when a fragment does not update them. | - | `done` | `CopilotSSEDoneEvent` | Terminal success event. Emitted after all prior deltas. `finishReason` is the provider finish reason such as `stop` or `tool_calls`. | - | `error` | `CopilotSSEErrorEvent` | Terminal failure event emitted if streaming fails after the HTTP response has started. No `done` event follows `error`. | - - A successful stream may contain any number of `text_delta` and `tool_call_delta` events before the final `done` - event. Assistant responses may be text-only, tool-call-only, or contain both. - - Quota and rate limits: - - - Each message consumes 1 quota from the authenticated user's allowance. - - Per-user short-window rate limits also apply to prevent bursts. Hitting them returns 429 with `Retry-After`. - - Long-window quota limits vary based on the authenticated user's plan. - - When the long-window quota limit is reached, the 403 response includes a `Retry-After` header with the wait time in seconds. - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/CopilotMessageRequest" + - Projects + summary: Record a project view + description: Record a view for the specified project as the authenticated user. + parameters: + - name: owner + description: Username of the project's owner. + in: path + required: true + schema: + $ref: "#/components/schemas/Project/properties/owner" + - name: project + description: Name of the project to record a view for. + in: path + required: true + schema: + $ref: "#/components/schemas/Project/properties/name" responses: - "200": - description: | - Structured SSE stream established. - - Parse the stream as SSE frames and then decode each event's JSON `data` payload according to the event type. - content: - text/event-stream: - schema: - type: string - description: Server-Sent Events stream containing `text_delta`, `tool_call_delta`, `done`, and `error` events. - examples: - textOnly: - summary: Text-only assistant response - value: | - event: text_delta - data: {"text":"Hello"} - - event: text_delta - data: {"text":"!"} - - event: done - data: {"finishReason":"stop"} - textAndToolCall: - summary: Assistant text followed by a tool call - value: | - event: text_delta - data: {"text":"Let me check that for you."} - - event: tool_call_delta - data: {"index":0,"id":"call_1","function":{"name":"list_projects","arguments":"{\"query\":\"demo\"}"}} - - event: done - data: {"finishReason":"tool_calls"} - providerError: - summary: Stream failure after the response has started - value: | - event: error - data: {"reason":"streamFailed","message":"stream failed"} + "204": + description: Successfully recorded the project view. "401": $ref: "#/components/responses/Unauthorized" - "403": - description: Insufficient permissions or quota to perform this operation. - headers: - Retry-After: - description: Seconds to wait before retrying after hitting the long-window quota limit. - schema: - type: integer - "429": - description: Too many requests to perform this operation. - headers: - Retry-After: - description: Seconds to wait before retrying after hitting the short-window rate limit. - schema: - type: integer + "409": + $ref: "#/components/responses/ResourceMoved" - /ai-interaction/turns: - post: - operationId: createAIInteractionTurn + /projects/{owner}/{project}/releases: + get: + operationId: listProjectReleases tags: - - AI Interaction - summary: Perform an AI interaction turn - description: | - Send a message and context to the AI, receive a response including text and an optional command. - - Quota and rate limits: - - - Each turn consumes 1 quota from the authenticated user's AI interaction turn allowance. - - Per-user short-window rate limits also apply to prevent bursts. Hitting them returns 429 with `Retry-After`. - - Long-window quota limits vary based on the authenticated user's plan. - - When the long-window quota limit is reached, the 403 response includes a `Retry-After` header with the wait time in seconds. - requestBody: + - Project Releases + summary: List project releases + description: Retrieve a list of releases for a project. + security: + - {} + - bearerAuth: [] + parameters: + - name: owner + description: Username of the project's owner. + in: path + required: true + schema: + $ref: "#/components/schemas/Project/properties/owner" + - name: project + description: Name of the project. + in: path + required: true + schema: + $ref: "#/components/schemas/Project/properties/name" + - name: orderBy + description: Field by which to order the results. + in: query + schema: + type: string + enum: + - createdAt + - updatedAt + - remixCount + default: createdAt + examples: + - createdAt + - $ref: "#/components/parameters/SortOrder" + - $ref: "#/components/parameters/PageIndex" + - $ref: "#/components/parameters/PageSize" + responses: + "200": + description: Successful response. + content: + application/json: + schema: + type: object + required: + - total + - data + properties: + total: + $ref: "#/components/schemas/ByPage/properties/total" + data: + type: array + items: + $ref: "#/components/schemas/ProjectRelease" + "301": + $ref: "#/components/responses/MovedPermanently" + "401": + $ref: "#/components/responses/Unauthorized" + post: + operationId: createProjectRelease + tags: + - Project Releases + summary: Create a project release + description: Create a new release for a specific project. + parameters: + - name: owner + description: Username of the project's owner. + in: path + required: true + schema: + $ref: "#/components/schemas/Project/properties/owner" + - name: project + description: Name of the project. + in: path + required: true + schema: + $ref: "#/components/schemas/Project/properties/name" + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - name + - description + properties: + name: + $ref: "#/components/schemas/ProjectRelease/properties/name" + description: + $ref: "#/components/schemas/ProjectRelease/properties/description" + thumbnail: + $ref: "#/components/schemas/ProjectRelease/properties/thumbnail" + responses: + "201": + description: Successfully created the project release. + headers: + Location: + description: Canonical route of the created project release. + schema: + type: string + format: uri-reference + minLength: 1 + examples: + - /projects/john/NiuXiaoQi/releases/v1.2.3 + content: + application/json: + schema: + $ref: "#/components/schemas/ProjectRelease" + "401": + $ref: "#/components/responses/Unauthorized" + "409": + description: | + Returned when the project path references a moved project. + + The response includes a `canonical` payload for the project route. + + Clients may update the request and resubmit it explicitly. + content: + application/json: + schema: + $ref: "#/components/schemas/MovedResourceError" + + /projects/{owner}/{project}/releases/{release}: + get: + operationId: getProjectRelease + tags: + - Project Releases + summary: Get a project release + description: Retrieve details of a specific project release. + security: + - {} + - bearerAuth: [] + parameters: + - name: owner + description: Username of the project's owner. + in: path + required: true + schema: + $ref: "#/components/schemas/Project/properties/owner" + - name: project + description: Name of the project. + in: path + required: true + schema: + $ref: "#/components/schemas/Project/properties/name" + - name: release + description: Name of the project release to get. + in: path + required: true + schema: + $ref: "#/components/schemas/ProjectRelease/properties/name" + responses: + "200": + description: Successful response. + content: + application/json: + schema: + $ref: "#/components/schemas/ProjectRelease" + "301": + $ref: "#/components/responses/MovedPermanently" + "401": + $ref: "#/components/responses/Unauthorized" + + /assets: + get: + operationId: listAssets + tags: + - Assets + summary: List assets + description: | + Retrieve a list of assets visible to the request. + + Anonymous requests return public assets. Authenticated requests return public assets and the authenticated + user's own assets when no visibility filter is provided. A non-public visibility filter requires authentication + and is scoped to the authenticated user's own assets. + security: + - {} + - bearerAuth: [] + parameters: + - name: keyword + description: Filter assets by display name pattern. + in: query + schema: + $ref: "#/components/schemas/Asset/properties/displayName" + - name: type + description: Filter assets by type. + in: query + schema: + $ref: "#/components/schemas/Asset/properties/type" + - name: category + description: Filter assets by category. + in: query + schema: + $ref: "#/components/schemas/Asset/properties/category" + - name: filesHash + description: Filter assets by files hash. + in: query + schema: + $ref: "#/components/schemas/Asset/properties/filesHash" + - name: visibility + description: Filter assets by visibility. + in: query + schema: + $ref: "#/components/schemas/Asset/properties/visibility" + - name: orderBy + description: Field by which to order the results. + in: query + schema: + type: string + enum: + - createdAt + - updatedAt + - displayName + default: createdAt + examples: + - createdAt + - $ref: "#/components/parameters/SortOrder" + - $ref: "#/components/parameters/PageIndex" + - $ref: "#/components/parameters/PageSize" + responses: + "200": + description: Successful response. + content: + application/json: + schema: + type: object + required: + - total + - data + properties: + total: + $ref: "#/components/schemas/ByPage/properties/total" + data: + type: array + items: + $ref: "#/components/schemas/Asset" + "401": + $ref: "#/components/responses/Unauthorized" + + /assets/{assetID}: + get: + operationId: getAsset + tags: + - Assets + summary: Get an asset + description: Retrieve details of a specific asset. + security: + - {} + - bearerAuth: [] + parameters: + - name: assetID + description: ID of the asset to get. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + responses: + "200": + description: Successful response. + content: + application/json: + schema: + $ref: "#/components/schemas/Asset" + "401": + $ref: "#/components/responses/Unauthorized" + patch: + operationId: updateAsset + tags: + - Assets + summary: Update an asset + description: | + Update the details of a specific asset. + + Permission requirements: + + - Only users with the `assetAdmin` role can update assets to public visibility. + - Asset owners can update their private assets. + parameters: + - name: assetID + description: ID of the asset to update. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + requestBody: + required: true + content: + application/json: + schema: + type: object + minProperties: 1 + properties: + displayName: + $ref: "#/components/schemas/Asset/properties/displayName" + type: + $ref: "#/components/schemas/Asset/properties/type" + category: + $ref: "#/components/schemas/Asset/properties/category" + description: + $ref: "#/components/schemas/Asset/properties/description" + extraSettings: + $ref: "#/components/schemas/Asset/properties/extraSettings" + files: + $ref: "#/components/schemas/Asset/properties/files" + filesHash: + $ref: "#/components/schemas/Asset/properties/filesHash" + visibility: + $ref: "#/components/schemas/Asset/properties/visibility" + responses: + "200": + description: Successfully updated the asset. + content: + application/json: + schema: + $ref: "#/components/schemas/Asset" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. + delete: + operationId: deleteAsset + tags: + - Assets + summary: Delete an asset + description: Permanently delete a specific asset. + parameters: + - name: assetID + description: ID of the asset to delete. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + responses: + "204": + description: Successfully deleted the asset. + "401": + $ref: "#/components/responses/Unauthorized" + + /courses: + get: + operationId: listCourses + tags: + - Courses + summary: List courses + description: Retrieve a list of courses visible to the request. + security: + - {} + - bearerAuth: [] + parameters: + - name: courseSeriesID + description: Filter courses by the course series ID. + in: query + schema: + $ref: "#/components/schemas/Model/properties/id" + - name: orderBy + description: Field by which to order the results. + in: query + schema: + type: string + enum: + - createdAt + - updatedAt + - sequenceInCourseSeries + default: createdAt + examples: + - createdAt + - $ref: "#/components/parameters/SortOrder" + - $ref: "#/components/parameters/PageIndex" + - $ref: "#/components/parameters/PageSize" + responses: + "200": + description: Successful response. + content: + application/json: + schema: + type: object + required: + - total + - data + properties: + total: + $ref: "#/components/schemas/ByPage/properties/total" + data: + type: array + items: + $ref: "#/components/schemas/Course" + + /courses/{courseID}: + get: + operationId: getCourse + tags: + - Courses + summary: Get a course + description: Retrieve details of a specific course. + security: + - {} + - bearerAuth: [] + parameters: + - name: courseID + description: ID of the course to get. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + responses: + "200": + description: Successful response. + content: + application/json: + schema: + $ref: "#/components/schemas/Course" + patch: + operationId: updateCourse + tags: + - Courses + summary: Update a course + description: | + Update the details of a specific course. + + Permission requirements: + + - Only users with the `courseAdmin` role can update courses. + parameters: + - name: courseID + description: ID of the course to update. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + requestBody: + required: true + content: + application/json: + schema: + type: object + minProperties: 1 + properties: + title: + $ref: "#/components/schemas/Course/properties/title" + thumbnail: + $ref: "#/components/schemas/Course/properties/thumbnail" + entrypoint: + $ref: "#/components/schemas/Course/properties/entrypoint" + references: + $ref: "#/components/schemas/Course/properties/references" + prompt: + $ref: "#/components/schemas/Course/properties/prompt" + responses: + "200": + description: Successfully updated the course. + content: + application/json: + schema: + $ref: "#/components/schemas/Course" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. + delete: + operationId: deleteCourse + tags: + - Courses + summary: Delete a course + description: | + Permanently delete a specific course. + + Permission requirements: + + - Only users with the `courseAdmin` role can delete courses. + parameters: + - name: courseID + description: ID of the course to delete. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + responses: + "204": + description: Successfully deleted the course. + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. + + /course-series: + get: + operationId: listCourseSeries + tags: + - Course Series + summary: List course series + description: Retrieve a list of course series visible to the request. + security: + - {} + - bearerAuth: [] + parameters: + - name: orderBy + description: Field by which to order the results. + in: query + schema: + type: string + enum: + - createdAt + - updatedAt + - order + default: order + examples: + - order + - $ref: "#/components/parameters/SortOrder" + - $ref: "#/components/parameters/PageIndex" + - $ref: "#/components/parameters/PageSize" + responses: + "200": + description: Successful response. + content: + application/json: + schema: + type: object + required: + - total + - data + properties: + total: + $ref: "#/components/schemas/ByPage/properties/total" + data: + type: array + items: + $ref: "#/components/schemas/CourseSeries" + + /course-series/{courseSeriesID}: + get: + operationId: getCourseSeries + tags: + - Course Series + summary: Get a course series + description: Retrieve details of a specific course series. + security: + - {} + - bearerAuth: [] + parameters: + - name: courseSeriesID + description: ID of the course series to get. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + responses: + "200": + description: Successful response. + content: + application/json: + schema: + $ref: "#/components/schemas/CourseSeries" + patch: + operationId: updateCourseSeries + tags: + - Course Series + summary: Update a course series + description: | + Update the details of a specific course series. + + Permission requirements: + + - Only users with the `courseAdmin` role can update course series. + parameters: + - name: courseSeriesID + description: ID of the course series to update. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + requestBody: + required: true + content: + application/json: + schema: + type: object + minProperties: 1 + properties: + title: + $ref: "#/components/schemas/CourseSeries/properties/title" + thumbnail: + $ref: "#/components/schemas/CourseSeries/properties/thumbnail" + description: + $ref: "#/components/schemas/CourseSeries/properties/description" + courseIDs: + $ref: "#/components/schemas/CourseSeries/properties/courseIDs" + order: + $ref: "#/components/schemas/CourseSeries/properties/order" + responses: + "200": + description: Successfully updated the course series. + content: + application/json: + schema: + $ref: "#/components/schemas/CourseSeries" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. + delete: + operationId: deleteCourseSeries + tags: + - Course Series + summary: Delete a course series + description: | + Permanently delete a specific course series. + + Permission requirements: + + - Only users with the `courseAdmin` role can delete course series. + parameters: + - name: courseSeriesID + description: ID of the course series to delete. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + responses: + "204": + description: Successfully deleted the course series. + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. + + /copilot/messages: + post: + operationId: createCopilotMessage + tags: + - Copilot + summary: Generate a structured SSE stream message + description: | + Generate a structured SSE stream message by sending a list of input messages. + + The response is a Server-Sent Events stream where each `data:` line contains a single JSON object. + + Event semantics: + + | Event | Data schema | Semantics | + |-------|-------------|-----------| + | `text_delta` | `CopilotSSETextDeltaEvent` | Incremental assistant text fragment. Concatenate `text` values in order to reconstruct `content.text`. | + | `tool_call_delta` | `CopilotSSEToolCallDeltaEvent` | Incremental fragment for one assistant tool call. Merge fragments by `index`; `id`, `function.name`, and `function.arguments` are omitted when a fragment does not update them. | + | `done` | `CopilotSSEDoneEvent` | Terminal success event. Emitted after all prior deltas. `finishReason` is the provider finish reason such as `stop` or `tool_calls`. | + | `error` | `CopilotSSEErrorEvent` | Terminal failure event emitted if streaming fails after the HTTP response has started. No `done` event follows `error`. | + + A successful stream may contain any number of `text_delta` and `tool_call_delta` events before the final `done` + event. Assistant responses may be text-only, tool-call-only, or contain both. + + Quota and rate limits: + + - Each message consumes 1 quota from the authenticated user's allowance. + - Per-user short-window rate limits also apply to prevent bursts. Hitting them returns 429 with `Retry-After`. + - Long-window quota limits vary based on the authenticated user's plan. + - When the long-window quota limit is reached, the 403 response includes a `Retry-After` header with the wait time in seconds. + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CopilotMessageRequest" + responses: + "200": + description: | + Structured SSE stream established. + + Parse the stream as SSE frames and then decode each event's JSON `data` payload according to the event type. + content: + text/event-stream: + schema: + type: string + description: Server-Sent Events stream containing `text_delta`, `tool_call_delta`, `done`, and `error` events. + examples: + textOnly: + summary: Text-only assistant response + value: | + event: text_delta + data: {"text":"Hello"} + + event: text_delta + data: {"text":"!"} + + event: done + data: {"finishReason":"stop"} + textAndToolCall: + summary: Assistant text followed by a tool call + value: | + event: text_delta + data: {"text":"Let me check that for you."} + + event: tool_call_delta + data: {"index":0,"id":"call_1","function":{"name":"list_projects","arguments":"{\"query\":\"demo\"}"}} + + event: done + data: {"finishReason":"tool_calls"} + providerError: + summary: Stream failure after the response has started + value: | + event: error + data: {"reason":"streamFailed","message":"stream failed"} + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions or quota to perform this operation. + headers: + Retry-After: + description: Seconds to wait before retrying after hitting the long-window quota limit. + schema: + type: integer + "429": + description: Too many requests to perform this operation. + headers: + Retry-After: + description: Seconds to wait before retrying after hitting the short-window rate limit. + schema: + type: integer + + /ai-interaction/turns: + post: + operationId: createAIInteractionTurn + tags: + - AI Interaction + summary: Perform an AI interaction turn + description: | + Send a message and context to the AI, receive a response including text and an optional command. + + Quota and rate limits: + + - Each turn consumes 1 quota from the authenticated user's AI interaction turn allowance. + - Per-user short-window rate limits also apply to prevent bursts. Hitting them returns 429 with `Retry-After`. + - Long-window quota limits vary based on the authenticated user's plan. + - When the long-window quota limit is reached, the 403 response includes a `Retry-After` header with the wait time in seconds. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - commandSpecs + allOf: + - if: + properties: + continuationTurn: + const: 0 + then: + required: + - content + properties: + content: + minLength: 1 + - if: + required: + - continuationTurn + properties: + continuationTurn: + minimum: 1 + then: + required: + - history + properties: + history: + minItems: 1 + properties: + content: + description: The core user input message. + type: string + maxLength: 280 + examples: + - What should I do next? + context: + description: Specific context for the current user input. + type: object + additionalProperties: true + examples: + - position: [10, 20] + health: 80 + role: + description: Persona the AI should adopt. + type: string + examples: + - Guide + roleContext: + description: Additional context specific to the role. + type: object + additionalProperties: true + examples: + - style: friendly + knowledgeScope: game rules + knowledgeBase: + description: Background knowledge provided to the AI. + type: object + additionalProperties: true + examples: + - worldName: TestWorld + gravity: 9.8 + commandSpecs: + description: Commands the AI is allowed to call. + type: array + minItems: 1 + items: + type: object + required: + - name + properties: + name: + description: Unique identifier for the command. + type: string + minLength: 1 + examples: + - Move + description: + description: Explanation of what the command does. + type: string + examples: + - Move the character + parameters: + description: Parameters the command accepts. + type: array + items: + type: object + required: + - name + - type + properties: + name: + description: Parameter name. + type: string + minLength: 1 + examples: + - Direction + type: + description: Go type name of the parameter. + type: string + minLength: 1 + examples: + - string + description: + description: Purpose of the parameter. + type: string + examples: + - "Direction to move: up, down, left, right" + history: + description: Record of previous interactions in this session. + type: array + items: + $ref: "#/components/schemas/AIInteractionTurn" + archivedHistory: + description: Content of archived historical interactions. + type: string + examples: + - "Previous conversation summary..." + continuationTurn: + description: | + Current turn number in a multi-turn interaction sequence. + + A value of 0 means this is the initial turn from user input. Values greater than 0 indicate + continuation turns where the AI continues the current interaction sequence based on command outcomes. + type: integer + format: int32 + minimum: 0 + default: 0 + examples: + - 0 + responses: + "200": + description: Successful AI interaction. + content: + application/json: + schema: + type: object + required: + - text + properties: + text: + description: Textual part of the AI's response. + type: string + examples: + - Okay, I suggest moving to (1, 1). + commandName: + description: Name of the command the AI wants to execute. + type: string + examples: + - MakeMove + commandArgs: + description: Arguments for the command to be executed. + type: object + additionalProperties: true + examples: + - Row: 1 + Col: 1 + Result: "" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions or quota to perform this operation. + headers: + Retry-After: + description: Seconds to wait before retrying after hitting the long-window quota limit. + schema: + type: integer + "429": + description: Too many requests to perform this operation. + headers: + Retry-After: + description: Seconds to wait before retrying after hitting the short-window rate limit. + schema: + type: integer + + /ai-interaction/archives: + post: + operationId: createAIInteractionArchive + tags: + - AI Interaction + summary: Archive AI interaction history + description: | + Archive a batch of interaction turns into a condensed summary for future context. + + Quota and rate limits: + + - Each archive request consumes 1 quota from the authenticated user's AI interaction archive allowance. + - Per-user short-window rate limits also apply to prevent bursts. Hitting them returns 429 with `Retry-After`. + - Long-window quota limits vary based on the authenticated user's plan. + - When the long-window quota limit is reached, the 403 response includes a `Retry-After` header with the wait time in seconds. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - turns + properties: + turns: + description: Interaction turns to be archived. + type: array + minItems: 1 + maxItems: 50 + items: + $ref: "#/components/schemas/AIInteractionTurn" + existingArchive: + description: Existing archived content to be merged with new turns. + type: + - string + - "null" + examples: + - "Previous conversation summary..." + responses: + "200": + description: Successfully archived interaction history. + content: + application/json: + schema: + type: object + required: + - content + properties: + content: + description: The consolidated archive content. + type: string + examples: + - "Complete conversation summary including new turns..." + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions or quota to perform this operation. + headers: + Retry-After: + description: Seconds to wait before retrying after hitting the long-window quota limit. + schema: + type: integer + "429": + description: Too many requests to perform this operation. + headers: + Retry-After: + description: Seconds to wait before retrying after hitting the short-window rate limit. + schema: + type: integer + + /aigc/tasks: + post: + operationId: createAIGCTask + tags: + - AIGC + summary: Create an AIGC task + description: | + Create an asynchronous AIGC task for resource-intensive generation and media-processing operations such as image + generation, video generation, background removal, and video frame extraction. + + Task-based generation and media-processing operations are processed asynchronously. Use the returned task ID to + poll for status via `GET /aigc/tasks/{taskID}` or subscribe to real-time updates via + `GET /aigc/tasks/{taskID}/events`. + + Supported task types: + + | Type | Description | + |------|-------------| + | `removeBackground` | Remove image background | + | `generateCostume` | Generate costume image | + | `generateAnimationVideo` | Generate animation video | + | `extractVideoFrames` | Extract frames from video | + | `generateBackdrop` | Generate backdrop image | + + Quota and rate limits: + + - Each task consumes quota from the authenticated user's AIGC allowance based on the task type. + - Per-user short-window rate limits apply. Hitting them returns 429 with `Retry-After`. + requestBody: + required: true + content: + application/json: + schema: + oneOf: + - title: removeBackground + type: object + required: + - type + - parameters + properties: + type: + type: string + enum: + - removeBackground + parameters: + description: Parameters for background removal. + type: object + required: + - imageUrl + properties: + imageUrl: + description: Universal URL of the image to remove background from, such as kodo://bucket/key. + type: string + format: uri + minLength: 1 + examples: + - kodo://bucket/character.png + - title: generateCostume + type: object + required: + - type + - parameters + properties: + type: + type: string + enum: + - generateCostume + parameters: + description: Parameters for costume generation. + type: object + required: + - settings + properties: + settings: + $ref: "#/components/schemas/AIGCCostumeSettings" + n: + description: Number of costume images to generate. + type: integer + minimum: 1 + maximum: 4 + default: 1 + examples: + - 4 + - title: generateAnimationVideo + type: object + required: + - type + - parameters + properties: + type: + type: string + enum: + - generateAnimationVideo + parameters: + description: Parameters for animation video generation. + type: object + required: + - settings + properties: + settings: + $ref: "#/components/schemas/AIGCAnimationSettings" + - title: extractVideoFrames + type: object + required: + - type + - parameters + properties: + type: + type: string + enum: + - extractVideoFrames + parameters: + description: Parameters for video frame extraction. + type: object + required: + - videoUrl + - duration + properties: + videoUrl: + description: Universal URL of the video to extract frames from, such as kodo://bucket/key. + type: string + format: uri + minLength: 1 + examples: + - kodo://bucket/animation.mp4 + startTime: + description: Start time in milliseconds from which to extract frames. Precision is 100ms. + type: integer + minimum: 0 + multipleOf: 100 + default: 0 + examples: + - 0 + - 1500 + duration: + description: Duration in milliseconds of the segment to extract frames from. Precision is 100ms. + type: integer + minimum: 0 + multipleOf: 100 + maximum: 60000 + examples: + - 3000 + - 10000 + interval: + description: Interval between frames in milliseconds. Precision is 100ms. + type: integer + minimum: 100 + multipleOf: 100 + maximum: 1000 + default: 100 + examples: + - 100 + - title: generateBackdrop + type: object + required: + - type + - parameters + properties: + type: + type: string + enum: + - generateBackdrop + parameters: + description: Parameters for backdrop generation. + type: object + required: + - settings + properties: + settings: + $ref: "#/components/schemas/AIGCBackdropSettings" + n: + description: Number of backdrop images to generate. + type: integer + minimum: 1 + maximum: 4 + default: 1 + examples: + - 4 + responses: + "202": + description: Task accepted and queued for processing. + headers: + Location: + description: Canonical route of the created AIGC task. + schema: + type: string + format: uri-reference + minLength: 1 + examples: + - /aigc/tasks/1 + content: + application/json: + schema: + $ref: "#/components/schemas/AIGCTask" + "400": + description: Invalid task type or parameters. + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions or quota. + headers: + Retry-After: + description: Seconds to wait before retrying after hitting the long-window quota limit. + schema: + type: integer + "429": + description: Too many requests. + headers: + Retry-After: + description: Seconds to wait before retrying after hitting the short-window rate limit. + schema: + type: integer + + /aigc/tasks/{taskID}: + get: + operationId: getAIGCTask + tags: + - AIGC + summary: Get AIGC task status + description: | + Get the current status of an AIGC task. + + Poll this endpoint to check if the task is complete. For real-time updates, use the SSE endpoint + `GET /aigc/tasks/{taskID}/events` instead. + parameters: + - name: taskID + description: The task ID to get. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + responses: + "200": + description: Task status retrieved. + content: + application/json: + schema: + $ref: "#/components/schemas/AIGCTask" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + description: Task not found. + + /aigc/tasks/{taskID}/events: + get: + operationId: subscribeAIGCTaskEvents + tags: + - AIGC + summary: Subscribe to AIGC task events + description: | + Subscribe to real-time task status updates via Server-Sent Events. + + The first event is always a `snapshot` containing the current task state. This eliminates the need to call + `GET /aigc/tasks/{taskID}` separately and avoids race conditions between fetching current state and subscribing + to updates. + + The connection remains open until the task completes, is cancelled, or fails. If the task is already in a + terminal state when connecting, the `snapshot` event will contain the final state and the connection will close + immediately. + + Event types: + + | Event | Data | Description | + |-------|------|-------------| + | `snapshot` | Full `AIGCTask` object | Initial state, always sent first | + | `cancelling` | `{}` | Task cancellation in progress | + | `completed` | `{"result": {...}}` | Task completed successfully | + | `cancelled` | `{}` | Task was cancelled | + | `failed` | `{"error": {"reason": "...", "message": "..."}}` | Task failed | + + Example: task in progress + + ``` + GET /aigc/tasks/123/events + + event: snapshot + data: {"id": "123", "status": "processing"} + + event: completed + data: {"result": {"imageUrls": ["kodo://bucket/path/to/file.png"]}} + ``` + + Example: task already completed + + ``` + GET /aigc/tasks/456/events + + event: snapshot + data: {"id": "456", "status": "completed", "result": {"imageUrls": ["kodo://bucket/path/to/file.png"]}} + + (connection closed) + ``` + + Example: task pending + + ``` + GET /aigc/tasks/789/events + + event: snapshot + data: {"id": "789", "status": "pending"} + + event: completed + data: {"result": {"imageUrls": ["kodo://bucket/path/to/file.png"]}} + ``` + + Example: task cancelled + + ``` + GET /aigc/tasks/123/events + + event: snapshot + data: {"id": "123", "status": "processing"} + + event: cancelling + data: {} + + event: cancelled + data: {} + + (connection closed) + ``` + parameters: + - name: taskID + description: The task ID to subscribe to. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + responses: + "200": + description: SSE stream established. + content: + text/event-stream: + schema: + type: string + description: Server-Sent Events stream. + "401": + $ref: "#/components/responses/Unauthorized" + "404": + description: Task not found. + + /aigc/tasks/{taskID}/cancellation: + put: + operationId: cancelAIGCTask + tags: + - AIGC + summary: Cancel an AIGC task + description: | + Cancel a pending or processing AIGC task. + + If subscribed via `GET /aigc/tasks/{taskID}/events`, a `cancelling` event will be emitted when cancellation + begins, followed by a `cancelled` event when complete. + + Cancellation rules: + + | Current Status | Result | + |----------------|--------| + | `pending` | Immediately `cancelled`, quota refunded | + | `processing` | Transitions to `cancelling`, then `cancelled` | + | `cancelling` | No-op, returns current state | + | `completed` | `409 Conflict` | + | `cancelled` | No-op, idempotent, returns current state | + | `failed` | `409 Conflict` | + + Quota refund: + + - Tasks cancelled from `pending` status receive a full quota refund. + - Tasks cancelled from `processing` status do not receive a refund because resources are already consumed. + parameters: + - name: taskID + description: The task ID to cancel. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + responses: + "202": + description: Cancellation request accepted. + content: + application/json: + schema: + $ref: "#/components/schemas/AIGCTask" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + description: Task not found. + "409": + description: Task cannot be cancelled because it has already completed or failed. + + /aigc/project-descriptions: + post: + operationId: generateProjectDescription + tags: + - AIGC + summary: Generate project description + description: | + Generate an AI-powered descriptive summary of a game from the player's perspective based on provided game content + such as spx source code and project structure. + + Quota and rate limits: + + - Each request consumes 1 quota from the authenticated user's AI description allowance. + - Per-user short-window rate limits also apply to prevent bursts. Hitting them returns 429 with `Retry-After`. + - Quota limits vary based on the authenticated user's plan. + - When the long-window quota limit is reached, the 403 response includes a `Retry-After` header with the wait time in seconds. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - content + properties: + content: + description: The game content to generate a description for, such as spx source code or project structure. + type: string + minLength: 1 + maxLength: 150000 + examples: + - | + Game: TicTacToe + Description: A classic Tic-Tac-Toe game. + + === File: main.spx === + var ( + // board is the main Tic-Tac-Toe board, where 1 represents "X" and 2 represents "O". + board [3][3]int + ) + + // onStart handles the game start event. + onStart => { + // ... + } + knowledge: + description: Optional background knowledge used to ground project description generation. + type: string + maxLength: 120000 + examples: + - | + # About spx + + spx is a Scratch-like 2D Game Engine for STEM education. + responses: + "200": + description: Successfully generated project description. + content: + application/json: + schema: + type: object + required: + - description + properties: + description: + description: AI-generated descriptive summary of the game world from the player's perspective. + type: string + examples: + - | + This is a Tic-Tac-Toe game played on a 3x3 grid. The player uses `X` marks while the AI opponent + uses `O` marks. Players take turns placing their marks in empty squares, with the goal of getting + three marks in a row horizontally, vertically, or diagonally. The game supports mouse click + interaction. Click on an empty square to place your mark. The game ends when either player + achieves three in a row or the board is completely filled. + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions or quota to perform this operation. + headers: + Retry-After: + description: Seconds to wait before retrying after hitting the long-window quota limit. + schema: + type: integer + "429": + description: Too many requests to perform this operation. + headers: + Retry-After: + description: Seconds to wait before retrying after hitting the short-window rate limit. + schema: + type: integer + + /aigc/asset-descriptions: + post: + operationId: generateAssetDescription + tags: + - AIGC + summary: Generate asset description + description: | + Generate a description for an asset using AI based on the provided text prompt and image input. + + This endpoint accepts multimodal input to generate a contextual description suitable for embedding and search + operations. The text prompt typically includes the asset's display name and additional context about how the + description will be used. + + Image requirements: + + - Format: PNG only (`image/png`) + - Maximum image size: 5 MB after decoding the base64 content + - Images must be provided as base64-encoded data URLs + - Other formats must be converted to PNG before sending + + Quota and rate limits: + + - Each request consumes 1 quota from the authenticated user's AIGC allowance. + - Per-user short-window rate limits apply. Hitting them returns 429 with `Retry-After`. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - content + properties: + content: + type: array + description: Array of content items including text and image inputs. + minItems: 1 + maxItems: 10 + items: + oneOf: + - $ref: "#/components/schemas/InputText" + - $ref: "#/components/schemas/InputImage" + discriminator: + propertyName: type + mapping: + inputText: "#/components/schemas/InputText" + inputImage: "#/components/schemas/InputImage" + examples: + tornado: + summary: Generate description for a tornado sprite + value: + content: + - type: inputText + text: "Generate a description for a sprite named 'Tornado'." + - type: inputImage + imageUrl: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" + responses: + "200": + description: Successfully generated description. + content: + application/json: + schema: + type: object + required: + - description + properties: + description: + type: string + description: The AI-generated description for the asset. + examples: + - "A powerful rotating column of air extending from a thunderstorm to the ground, characterized by its distinctive funnel shape and destructive force." + "400": + description: Invalid request parameters. + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions or quota to perform this operation. + headers: + Retry-After: + description: Seconds to wait before retrying after hitting the long-window quota limit. + schema: + type: integer + "429": + description: Too many requests to perform this operation. + headers: + Retry-After: + description: Seconds to wait before retrying after hitting the short-window rate limit. + schema: + type: integer + + /aigc/asset-adoptions: + post: + operationId: createAIGCAssetAdoption + tags: + - AIGC + summary: Adopt an AIGC-generated asset + description: | + Record that an AIGC-generated asset has been adopted. + The backend verifies task ownership and file provenance and may publish the adopted asset to the public library. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - taskIds + - asset + properties: + taskIds: + description: IDs of relevant AIGC tasks that produced the asset. + type: array + items: + $ref: "#/components/schemas/Model/properties/id" + examples: + - - "1" + - "2" + minItems: 1 + maxItems: 100 + asset: + description: Asset data. + type: object + required: + - displayName + - type + - description + - extraSettings + - filesHash + - files + properties: + displayName: + $ref: "#/components/schemas/Asset/properties/displayName" + type: + $ref: "#/components/schemas/Asset/properties/type" + description: + allOf: + - $ref: "#/components/schemas/Asset/properties/description" + minLength: 1 + extraSettings: + $ref: "#/components/schemas/Asset/properties/extraSettings" + filesHash: + $ref: "#/components/schemas/Asset/properties/filesHash" + files: + allOf: + - $ref: "#/components/schemas/Asset/properties/files" + minProperties: 1 + responses: + "204": + description: Asset adoption recorded. + "400": + description: Invalid parameters. + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions for given tasks. + + /aigc/asset-settings-enrichments: + post: + operationId: enrichAssetSettings + tags: + - AIGC + summary: Enrich asset settings + description: | + Use AI to enrich partial asset settings with detailed descriptions and parameters. + + Quota and rate limits: + + - Each request consumes 1 quota from the authenticated user's AIGC allowance. + - Per-user short-window rate limits apply. Hitting them returns 429 with `Retry-After`. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - assetType + - input + allOf: + - if: + required: + - assetType + properties: + assetType: + const: sprite + then: + properties: + settings: + $ref: "#/components/schemas/AIGCSpriteSettings" + - if: + required: + - assetType + properties: + assetType: + const: costume + then: + properties: + settings: + $ref: "#/components/schemas/AIGCCostumeSettings" + - if: + required: + - assetType + properties: + assetType: + const: animation + then: + properties: + settings: + $ref: "#/components/schemas/AIGCAnimationSettings" + - if: + required: + - assetType + properties: + assetType: + const: backdrop + then: + properties: + settings: + $ref: "#/components/schemas/AIGCBackdropSettings" + properties: + assetType: + description: Type of asset to enrich settings for. + type: string + enum: + - sprite + - costume + - animation + - backdrop + input: + description: User's text description of the asset. + type: string + minLength: 1 + examples: + - A fire demon character with glowing ember cracks + settings: + description: Optional partial settings to be enriched. Structure depends on assetType. + type: object + spriteSettings: + description: Optional existing sprite settings to provide context for costume or animation enrichment. + $ref: "#/components/schemas/AIGCSpriteSettings" + projectSettings: + description: Optional project settings to provide context for enrichment. + $ref: "#/components/schemas/AIGCProjectSettings" + lang: + description: | + Language the AI should use for all generated text. + When provided, the output strictly follows this language regardless of the input language. + When omitted, the AI infers language from the user input. + type: string + enum: + - en + - zh + responses: + "200": + description: Successfully enriched settings. + content: + application/json: + schema: + anyOf: + - $ref: "#/components/schemas/AIGCSpriteSettings" + - $ref: "#/components/schemas/AIGCCostumeSettings" + - $ref: "#/components/schemas/AIGCAnimationSettings" + - $ref: "#/components/schemas/AIGCBackdropSettings" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions or quota. + headers: + Retry-After: + description: Seconds to wait before retrying after hitting the long-window quota limit. + schema: + type: integer + "429": + description: Too many requests. + headers: + Retry-After: + description: Seconds to wait before retrying after hitting the short-window rate limit. + schema: + type: integer + + /aigc/sprite-content-settings: + post: + operationId: generateSpriteContentSettings + tags: + - AIGC + summary: Generate sprite content settings + description: | + Generate costume and animation settings for a sprite based on its settings. + + Quota and rate limits: + + - Each request consumes 1 quota from the authenticated user's AIGC allowance. + - Per-user short-window rate limits apply. Hitting them returns 429 with `Retry-After`. + requestBody: required: true content: application/json: schema: type: object required: - - commandSpecs - allOf: - - if: - properties: - continuationTurn: - const: 0 - then: - required: - - content - properties: - content: - minLength: 1 - - if: - required: - - continuationTurn - properties: - continuationTurn: - minimum: 1 - then: - required: - - history - properties: - history: - minItems: 1 + - settings properties: - content: - description: The core user input message. - type: string - maxLength: 280 - examples: - - What should I do next? - context: - description: Specific context for the current user input. - type: object - additionalProperties: true - examples: - - position: [10, 20] - health: 80 - role: - description: Persona the AI should adopt. - type: string - examples: - - Guide - roleContext: - description: Additional context specific to the role. - type: object - additionalProperties: true - examples: - - style: friendly - knowledgeScope: game rules - knowledgeBase: - description: Background knowledge provided to the AI. - type: object - additionalProperties: true - examples: - - worldName: TestWorld - gravity: 9.8 - commandSpecs: - description: Commands the AI is allowed to call. - type: array - minItems: 1 - items: - type: object - required: - - name - properties: - name: - description: Unique identifier for the command. - type: string - minLength: 1 - examples: - - Move - description: - description: Explanation of what the command does. - type: string - examples: - - Move the character - parameters: - description: Parameters the command accepts. - type: array - items: - type: object - required: - - name - - type - properties: - name: - description: Parameter name. - type: string - minLength: 1 - examples: - - Direction - type: - description: Go type name of the parameter. - type: string - minLength: 1 - examples: - - string - description: - description: Purpose of the parameter. - type: string - examples: - - "Direction to move: up, down, left, right" - history: - description: Record of previous interactions in this session. - type: array - items: - $ref: "#/components/schemas/AIInteractionTurn" - archivedHistory: - description: Content of archived historical interactions. - type: string - examples: - - "Previous conversation summary..." - continuationTurn: + settings: + $ref: "#/components/schemas/AIGCSpriteSettings" + lang: description: | - Current turn number in a multi-turn interaction sequence. - - A value of 0 means this is the initial turn from user input. Values greater than 0 indicate - continuation turns where the AI continues the current interaction sequence based on command outcomes. - type: integer - format: int32 - minimum: 0 - default: 0 - examples: - - 0 + Language the AI should use for all generated text. + When provided, the output strictly follows this language regardless of the input language. + When omitted, the AI infers language from the user input. + type: string + enum: + - en + - zh responses: "200": - description: Successful AI interaction. + description: Successfully generated content settings. content: application/json: schema: type: object required: - - text + - costumes + - animations + - animationBindings properties: - text: - description: Textual part of the AI's response. - type: string - examples: - - Okay, I suggest moving to (1, 1). - commandName: - description: Name of the command the AI wants to execute. - type: string - examples: - - MakeMove - commandArgs: - description: Arguments for the command to be executed. + costumes: + description: Settings for costumes to generate. + type: array + items: + $ref: "#/components/schemas/AIGCCostumeSettings" + animations: + description: Settings for animations to generate. + type: array + items: + $ref: "#/components/schemas/AIGCAnimationSettings" + animationBindings: + description: | + Recommended mapping from sprite state enum values to animation names. + Keys are sprite state enum values; values are animation name strings that should match the `name` + field from the animations array, omitted if no animation is bound to that state. type: object - additionalProperties: true - examples: - - Row: 1 - Col: 1 - Result: "" + additionalProperties: false + properties: + default: + type: string + step: + type: string + die: + type: string + example: + default: "idle" + step: "walk" "401": $ref: "#/components/responses/Unauthorized" "403": - description: Insufficient permissions or quota to perform this operation. + description: Insufficient permissions or quota. headers: Retry-After: description: Seconds to wait before retrying after hitting the long-window quota limit. schema: type: integer "429": - description: Too many requests to perform this operation. + description: Too many requests. headers: Retry-After: description: Seconds to wait before retrying after hitting the short-window rate limit. schema: type: integer - /ai-interaction/archives: + /upload-sessions: post: - operationId: createAIInteractionArchive + operationId: createUploadSession tags: - - AI Interaction - summary: Archive AI interaction history - description: | - Archive a batch of interaction turns into a condensed summary for future context. - - Quota and rate limits: + - Files + summary: Create an upload session + description: Create short-lived upload credentials and configuration for file uploads. + responses: + "201": + description: Upload session created. + content: + application/json: + schema: + $ref: "#/components/schemas/UpInfo" + "401": + $ref: "#/components/responses/Unauthorized" - - Each archive request consumes 1 quota from the authenticated user's AI interaction archive allowance. - - Per-user short-window rate limits also apply to prevent bursts. Hitting them returns 429 with `Retry-After`. - - Long-window quota limits vary based on the authenticated user's plan. - - When the long-window quota limit is reached, the 403 response includes a `Retry-After` header with the wait time in seconds. + /file-url-signatures: + post: + operationId: createFileURLSignatures + tags: + - Files + summary: Generate signed URLs for files + description: Create signed web URLs for the specified files to allow secure access. + security: [] requestBody: required: true content: @@ -2605,558 +4232,535 @@ paths: schema: type: object required: - - turns - properties: - turns: - description: Interaction turns to be archived. + - objects + properties: + objects: + description: List of universal URLs of the objects. type: array - minItems: 1 - maxItems: 50 items: - $ref: "#/components/schemas/AIInteractionTurn" - existingArchive: - description: Existing archived content to be merged with new turns. - type: - - string - - "null" + type: string + minLength: 1 + format: uri + pattern: "^kodo://[^/?#@:]+/[^?#]+$" examples: - - "Previous conversation summary..." + - - kodo://xbuilder-usercontent-test/files/FjgMMuSaAsRWx1t7UAQnQ5r4YsAe-195 responses: "200": - description: Successfully archived interaction history. + description: Successfully generated signed URLs for the files. content: application/json: schema: type: object required: - - content + - objectUrls properties: - content: - description: The consolidated archive content. - type: string + objectUrls: + description: Map from universal URLs to signed web URLs for the objects. + type: object + propertyNames: + type: string + format: uri + pattern: "^kodo://[^/?#@:]+/[^?#]+$" + additionalProperties: + type: string + format: uri examples: - - "Complete conversation summary including new turns..." - "401": - $ref: "#/components/responses/Unauthorized" - "403": - description: Insufficient permissions or quota to perform this operation. - headers: - Retry-After: - description: Seconds to wait before retrying after hitting the long-window quota limit. - schema: - type: integer - "429": - description: Too many requests to perform this operation. - headers: - Retry-After: - description: Seconds to wait before retrying after hitting the short-window rate limit. - schema: - type: integer + - kodo://xbuilder-usercontent-test/files/FjgMMuSaAsRWx1t7UAQnQ5r4YsAe-195: https://xbuilder-usercontent-test.gopluscdn.com/files/FjgMMuSaAsRWx1t7UAQnQ5r4YsAe-195?e=1727658000&token=t_6AOOkCdDu4m7fPleblcK0gMBZfbGQzeEIVt5Au:EMJdLqcCWrqQ5pRd01diOv7nhQw= - /aigc/tasks: + /project-conversions: post: - operationId: createAIGCTask + operationId: convertProject tags: - - AIGC - summary: Create an AIGC task + - Files + summary: Convert a project file description: | - Create an asynchronous AIGC task for resource-intensive generation and media-processing operations such as image - generation, video generation, background removal, and video frame extraction. - - Task-based generation and media-processing operations are processed asynchronously. Use the returned task ID to - poll for status via `GET /aigc/tasks/{taskID}` or subscribe to real-time updates via - `GET /aigc/tasks/{taskID}/events`. - - Supported task types: - - | Type | Description | - |------|-------------| - | `removeBackground` | Remove image background | - | `generateCostume` | Generate costume image | - | `generateAnimationVideo` | Generate animation video | - | `extractVideoFrames` | Extract frames from video | - | `generateBackdrop` | Generate backdrop image | - - Quota and rate limits: + Convert a Scratch format file to XBP (XBuilder Project) format. - - Each task consumes quota from the authenticated user's AIGC allowance based on the task type. - - Per-user short-window rate limits apply. Hitting them returns 429 with `Retry-After`. + This endpoint accepts a Scratch project file, version 2 or 3, and converts it to the XBP format used by the + XBuilder platform. + security: + - {} + - bearerAuth: [] requestBody: required: true content: - application/json: + multipart/form-data: schema: - oneOf: - - title: removeBackground - type: object - required: - - type - - parameters - properties: - type: - type: string - enum: - - removeBackground - parameters: - description: Parameters for background removal. - type: object - required: - - imageUrl - properties: - imageUrl: - description: Universal URL of the image to remove background from, such as kodo://bucket/key. - type: string - format: uri - minLength: 1 - examples: - - kodo://bucket/character.png - - title: generateCostume - type: object - required: - - type - - parameters - properties: - type: - type: string - enum: - - generateCostume - parameters: - description: Parameters for costume generation. - type: object - required: - - settings - properties: - settings: - $ref: "#/components/schemas/AIGCCostumeSettings" - n: - description: Number of costume images to generate. - type: integer - minimum: 1 - maximum: 4 - default: 1 - examples: - - 4 - - title: generateAnimationVideo - type: object - required: - - type - - parameters - properties: - type: - type: string - enum: - - generateAnimationVideo - parameters: - description: Parameters for animation video generation. - type: object - required: - - settings - properties: - settings: - $ref: "#/components/schemas/AIGCAnimationSettings" - - title: extractVideoFrames - type: object - required: - - type - - parameters - properties: - type: - type: string - enum: - - extractVideoFrames - parameters: - description: Parameters for video frame extraction. - type: object - required: - - videoUrl - - duration - properties: - videoUrl: - description: Universal URL of the video to extract frames from, such as kodo://bucket/key. - type: string - format: uri - minLength: 1 - examples: - - kodo://bucket/animation.mp4 - startTime: - description: Start time in milliseconds from which to extract frames. Precision is 100ms. - type: integer - minimum: 0 - multipleOf: 100 - default: 0 - examples: - - 0 - - 1500 - duration: - description: Duration in milliseconds of the segment to extract frames from. Precision is 100ms. - type: integer - minimum: 0 - multipleOf: 100 - maximum: 60000 - examples: - - 3000 - - 10000 - interval: - description: Interval between frames in milliseconds. Precision is 100ms. - type: integer - minimum: 100 - multipleOf: 100 - maximum: 1000 - default: 100 - examples: - - 100 - - title: generateBackdrop - type: object - required: - - type - - parameters - properties: - type: - type: string - enum: - - generateBackdrop - parameters: - description: Parameters for backdrop generation. - type: object - required: - - settings - properties: - settings: - $ref: "#/components/schemas/AIGCBackdropSettings" - n: - description: Number of backdrop images to generate. - type: integer - minimum: 1 - maximum: 4 - default: 1 - examples: - - 4 + type: object + required: + - file + properties: + file: + description: Scratch project file, such as .sb2, .sb3, or project zip. Form field name must be `file`. Maximum size is 64 MiB. + type: string + format: binary + maxLength: 67108864 + responses: + "200": + description: Conversion successful. Returns a zip archive containing the XBP project file. + content: + application/zip: + schema: + type: string + format: binary + examples: + xbpFile: + summary: Example XBP zip file + + /admin/account/users: + get: + operationId: listAdminAccountUsers + tags: + - Account Admin + summary: List account users as admin + description: | + Retrieve a list of account users. + + Requires the `accountAdmin` role. + parameters: + - name: orderBy + description: Field by which to order the results. + in: query + schema: + type: string + enum: + - createdAt + - updatedAt + default: createdAt + examples: + - createdAt + - $ref: "#/components/parameters/SortOrder" + - $ref: "#/components/parameters/PageIndex" + - $ref: "#/components/parameters/PageSize" responses: - "202": - description: Task accepted and queued for processing. - headers: - Location: - description: Canonical route of the created AIGC task. - schema: - type: string - format: uri-reference - minLength: 1 - examples: - - /aigc/tasks/1 + "200": + description: Successful response. content: application/json: schema: - $ref: "#/components/schemas/AIGCTask" - "400": - description: Invalid task type or parameters. + type: object + required: + - total + - data + properties: + total: + $ref: "#/components/schemas/ByPage/properties/total" + data: + type: array + items: + $ref: "#/components/schemas/AccountUser" "401": $ref: "#/components/responses/Unauthorized" "403": - description: Insufficient permissions or quota. - headers: - Retry-After: - description: Seconds to wait before retrying after hitting the long-window quota limit. - schema: - type: integer - "429": - description: Too many requests. - headers: - Retry-After: - description: Seconds to wait before retrying after hitting the short-window rate limit. + description: Insufficient permissions to perform this operation. + post: + operationId: createAdminAccountUser + tags: + - Account Admin + summary: Create an account user as admin + description: | + Create an account user. + + Requires the `accountAdmin` role. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - username + - displayName + properties: + username: + $ref: "#/components/schemas/AccountUser/properties/username" + displayName: + $ref: "#/components/schemas/AccountUser/properties/displayName" + avatar: + $ref: "#/components/schemas/AccountUser/properties/avatar" + password: + description: Optional administrator-managed password for the new user. + type: string + minLength: 8 + maxLength: 128 + responses: + "201": + description: Account user created. + content: + application/json: schema: - type: integer + $ref: "#/components/schemas/AccountUser" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. - /aigc/tasks/{taskID}: + /admin/account/users/{userID}: get: - operationId: getAIGCTask + operationId: getAdminAccountUser tags: - - AIGC - summary: Get AIGC task status + - Account Admin + summary: Get an account user as admin description: | - Get the current status of an AIGC task. + Retrieve an account user. - Poll this endpoint to check if the task is complete. For real-time updates, use the SSE endpoint - `GET /aigc/tasks/{taskID}/events` instead. + Requires the `accountAdmin` role. parameters: - - name: taskID - description: The task ID to get. + - name: userID + description: ID of the user to get. in: path required: true schema: $ref: "#/components/schemas/Model/properties/id" responses: "200": - description: Task status retrieved. + description: Successful response. content: application/json: schema: - $ref: "#/components/schemas/AIGCTask" + $ref: "#/components/schemas/AccountUser" "401": $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. "404": - description: Task not found. - - /aigc/tasks/{taskID}/events: - get: - operationId: subscribeAIGCTaskEvents + description: Account user not found. + patch: + operationId: updateAdminAccountUser tags: - - AIGC - summary: Subscribe to AIGC task events + - Account Admin + summary: Update an account user as admin description: | - Subscribe to real-time task status updates via Server-Sent Events. - - The first event is always a `snapshot` containing the current task state. This eliminates the need to call - `GET /aigc/tasks/{taskID}` separately and avoids race conditions between fetching current state and subscribing - to updates. - - The connection remains open until the task completes, is cancelled, or fails. If the task is already in a - terminal state when connecting, the `snapshot` event will contain the final state and the connection will close - immediately. - - Event types: - - | Event | Data | Description | - |-------|------|-------------| - | `snapshot` | Full `AIGCTask` object | Initial state, always sent first | - | `cancelling` | `{}` | Task cancellation in progress | - | `completed` | `{"result": {...}}` | Task completed successfully | - | `cancelled` | `{}` | Task was cancelled | - | `failed` | `{"error": {"reason": "...", "message": "..."}}` | Task failed | - - Example: task in progress - - ``` - GET /aigc/tasks/123/events - - event: snapshot - data: {"id": "123", "status": "processing"} - - event: completed - data: {"result": {"imageUrls": ["kodo://bucket/path/to/file.png"]}} - ``` - - Example: task already completed - - ``` - GET /aigc/tasks/456/events - - event: snapshot - data: {"id": "456", "status": "completed", "result": {"imageUrls": ["kodo://bucket/path/to/file.png"]}} - - (connection closed) - ``` - - Example: task pending - - ``` - GET /aigc/tasks/789/events + Update user fields managed by XBuilder Account. - event: snapshot - data: {"id": "789", "status": "pending"} + Requires the `accountAdmin` role. + parameters: + - name: userID + description: ID of the user to update. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + requestBody: + required: true + content: + application/json: + schema: + type: object + minProperties: 1 + properties: + username: + $ref: "#/components/schemas/AccountUser/properties/username" + displayName: + $ref: "#/components/schemas/AccountUser/properties/displayName" + avatar: + $ref: "#/components/schemas/AccountUser/properties/avatar" + responses: + "200": + description: Account user updated. + content: + application/json: + schema: + $ref: "#/components/schemas/AccountUser" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. + "404": + description: Account user not found. - event: completed - data: {"result": {"imageUrls": ["kodo://bucket/path/to/file.png"]}} - ``` + /admin/account/users/{userID}/password: + put: + operationId: setAdminAccountUserPassword + tags: + - Account Admin + summary: Set an account user password as admin + description: | + Set or replace the administrator-managed password credential for an account user. - Example: task cancelled + Requires the `accountAdmin` role. + parameters: + - name: userID + description: ID of the user whose password is set. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - password + properties: + password: + description: New administrator-managed password. + type: string + minLength: 8 + maxLength: 128 + responses: + "204": + description: Password credential set. + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. + "404": + description: Account user not found. + delete: + operationId: deleteAdminAccountUserPassword + tags: + - Account Admin + summary: Delete an account user password as admin + description: | + Remove the administrator-managed password credential from an account user. - ``` - GET /aigc/tasks/123/events + Requires the `accountAdmin` role. + parameters: + - name: userID + description: ID of the user whose password is deleted. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + responses: + "204": + description: Password credential deleted. + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. + "404": + description: Account user not found. - event: snapshot - data: {"id": "123", "status": "processing"} + /admin/account/users/{userID}/identities: + get: + operationId: listAdminAccountUserIdentities + tags: + - Account Admin + summary: List account user identities as admin + description: | + Retrieve a list of third-party identities linked to an account user. - event: cancelling - data: {} + Requires the `accountAdmin` role. + parameters: + - name: userID + description: ID of the user whose identities are listed. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + - $ref: "#/components/parameters/PageIndex" + - $ref: "#/components/parameters/PageSize" + responses: + "200": + description: Successful response. + content: + application/json: + schema: + type: object + required: + - total + - data + properties: + total: + $ref: "#/components/schemas/ByPage/properties/total" + data: + type: array + items: + $ref: "#/components/schemas/AccountUserIdentity" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. + "404": + description: Account user not found. - event: cancelled - data: {} + /admin/account/users/{userID}/identities/{identityID}: + delete: + operationId: deleteAdminAccountUserIdentity + tags: + - Account Admin + summary: Delete an account user identity as admin + description: | + Remove a third-party identity linked to an account user. - (connection closed) - ``` + Requires the `accountAdmin` role. parameters: - - name: taskID - description: The task ID to subscribe to. + - name: userID + description: ID of the user whose identity is deleted. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + - name: identityID + description: ID of the identity to delete. in: path required: true schema: $ref: "#/components/schemas/Model/properties/id" responses: - "200": - description: SSE stream established. - content: - text/event-stream: - schema: - type: string - description: Server-Sent Events stream. + "204": + description: Account user identity deleted. "401": $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. "404": - description: Task not found. + description: Account user or identity not found. - /aigc/tasks/{taskID}/cancellation: - put: - operationId: cancelAIGCTask + /admin/account/users/{userID}/sessions: + get: + operationId: listAdminAccountUserSessions tags: - - AIGC - summary: Cancel an AIGC task + - Account Admin + summary: List account user sessions as admin description: | - Cancel a pending or processing AIGC task. - - If subscribed via `GET /aigc/tasks/{taskID}/events`, a `cancelling` event will be emitted when cancellation - begins, followed by a `cancelled` event when complete. - - Cancellation rules: - - | Current Status | Result | - |----------------|--------| - | `pending` | Immediately `cancelled`, quota refunded | - | `processing` | Transitions to `cancelling`, then `cancelled` | - | `cancelling` | No-op, returns current state | - | `completed` | `409 Conflict` | - | `cancelled` | No-op, idempotent, returns current state | - | `failed` | `409 Conflict` | - - Quota refund: + Retrieve a list of account sessions of an account user. - - Tasks cancelled from `pending` status receive a full quota refund. - - Tasks cancelled from `processing` status do not receive a refund because resources are already consumed. + Requires the `accountAdmin` role. parameters: - - name: taskID - description: The task ID to cancel. + - name: userID + description: ID of the user whose account sessions are listed. in: path required: true schema: $ref: "#/components/schemas/Model/properties/id" + - $ref: "#/components/parameters/PageIndex" + - $ref: "#/components/parameters/PageSize" responses: - "202": - description: Cancellation request accepted. + "200": + description: Successful response. content: application/json: schema: - $ref: "#/components/schemas/AIGCTask" + type: object + required: + - total + - data + properties: + total: + $ref: "#/components/schemas/ByPage/properties/total" + data: + type: array + items: + $ref: "#/components/schemas/AccountSession" "401": $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. "404": - description: Task not found. - "409": - description: Task cannot be cancelled because it has already completed or failed. - - /aigc/project-descriptions: - post: - operationId: generateProjectDescription + description: Account user not found. + delete: + operationId: deleteAdminAccountUserSessions tags: - - AIGC - summary: Generate project description + - Account Admin + summary: Delete account user sessions as admin description: | - Generate an AI-powered descriptive summary of a game from the player's perspective based on provided game content - such as spx source code and project structure. + Revoke all account sessions of an account user. - Quota and rate limits: + Requires the `accountAdmin` role. + parameters: + - name: userID + description: ID of the user whose account sessions are deleted. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + responses: + "204": + description: Account user sessions deleted. + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. + "404": + description: Account user not found. - - Each request consumes 1 quota from the authenticated user's AI description allowance. - - Per-user short-window rate limits also apply to prevent bursts. Hitting them returns 429 with `Retry-After`. - - Quota limits vary based on the authenticated user's plan. - - When the long-window quota limit is reached, the 403 response includes a `Retry-After` header with the wait time in seconds. - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - content - properties: - content: - description: The game content to generate a description for, such as spx source code or project structure. - type: string - minLength: 1 - maxLength: 150000 - examples: - - | - Game: TicTacToe - Description: A classic Tic-Tac-Toe game. + /admin/account/sessions/{sessionID}: + delete: + operationId: deleteAdminAccountSession + tags: + - Account Admin + summary: Delete an account session as admin + description: | + Revoke an account session. - === File: main.spx === - var ( - // board is the main Tic-Tac-Toe board, where 1 represents "X" and 2 represents "O". - board [3][3]int - ) + Requires the `accountAdmin` role. + parameters: + - name: sessionID + description: ID of the account session to delete. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + responses: + "204": + description: Account session deleted. + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. + "404": + description: Account session not found. - // onStart handles the game start event. - onStart => { - // ... - } - knowledge: - description: Optional background knowledge used to ground project description generation. - type: string - maxLength: 120000 - examples: - - | - # About spx + /admin/account/apps: + get: + operationId: listAdminAccountApps + tags: + - Account Admin + summary: List account apps as admin + description: | + Retrieve account apps registered for first-party app SSO. - spx is a Scratch-like 2D Game Engine for STEM education. + Requires the `accountAdmin` role. + parameters: + - name: orderBy + description: Field by which to order the results. + in: query + schema: + type: string + enum: + - createdAt + - updatedAt + default: createdAt + examples: + - createdAt + - $ref: "#/components/parameters/SortOrder" + - $ref: "#/components/parameters/PageIndex" + - $ref: "#/components/parameters/PageSize" responses: "200": - description: Successfully generated project description. + description: Successful response. content: application/json: schema: type: object required: - - description + - total + - data properties: - description: - description: AI-generated descriptive summary of the game world from the player's perspective. - type: string - examples: - - | - This is a Tic-Tac-Toe game played on a 3x3 grid. The player uses `X` marks while the AI opponent - uses `O` marks. Players take turns placing their marks in empty squares, with the goal of getting - three marks in a row horizontally, vertically, or diagonally. The game supports mouse click - interaction. Click on an empty square to place your mark. The game ends when either player - achieves three in a row or the board is completely filled. + total: + $ref: "#/components/schemas/ByPage/properties/total" + data: + type: array + items: + $ref: "#/components/schemas/AccountApp" "401": $ref: "#/components/responses/Unauthorized" "403": - description: Insufficient permissions or quota to perform this operation. - headers: - Retry-After: - description: Seconds to wait before retrying after hitting the long-window quota limit. - schema: - type: integer - "429": - description: Too many requests to perform this operation. - headers: - Retry-After: - description: Seconds to wait before retrying after hitting the short-window rate limit. - schema: - type: integer - - /aigc/asset-descriptions: + description: Insufficient permissions to perform this operation. post: - operationId: generateAssetDescription + operationId: createAdminAccountApp tags: - - AIGC - summary: Generate asset description + - Account Admin + summary: Create an account app as admin description: | - Generate a description for an asset using AI based on the provided text prompt and image input. - - This endpoint accepts multimodal input to generate a contextual description suitable for embedding and search - operations. The text prompt typically includes the asset's display name and additional context about how the - description will be used. - - Image requirements: - - - Format: PNG only (`image/png`) - - Maximum image size: 5 MB after decoding the base64 content - - Images must be provided as base64-encoded data URLs - - Other formats must be converted to PNG before sending + Register an account app for first-party app SSO. - Quota and rate limits: - - - Each request consumes 1 quota from the authenticated user's AIGC allowance. - - Per-user short-window rate limits apply. Hitting them returns 429 with `Retry-After`. + Requires the `accountAdmin` role. requestBody: required: true content: @@ -3164,144 +4768,124 @@ paths: schema: type: object required: - - content + - name + - displayName + - clientType + - redirectURIs properties: - content: - type: array - description: Array of content items including text and image inputs. - minItems: 1 - maxItems: 10 - items: - oneOf: - - $ref: "#/components/schemas/InputText" - - $ref: "#/components/schemas/InputImage" - discriminator: - propertyName: type - mapping: - inputText: "#/components/schemas/InputText" - inputImage: "#/components/schemas/InputImage" - examples: - tornado: - summary: Generate description for a tornado sprite - value: - content: - - type: inputText - text: "Generate a description for a sprite named 'Tornado'." - - type: inputImage - imageUrl: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" + name: + $ref: "#/components/schemas/AccountApp/properties/name" + displayName: + $ref: "#/components/schemas/AccountApp/properties/displayName" + clientType: + $ref: "#/components/schemas/AccountApp/properties/clientType" + redirectURIs: + $ref: "#/components/schemas/AccountApp/properties/redirectURIs" + allowedOrigins: + $ref: "#/components/schemas/AccountApp/properties/allowedOrigins" + responses: + "201": + description: Account app created. + content: + application/json: + schema: + $ref: "#/components/schemas/AccountApp" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. + + /admin/account/apps/{appID}: + get: + operationId: getAdminAccountApp + tags: + - Account Admin + summary: Get an account app as admin + description: | + Retrieve an account app registered for first-party app SSO. + + Requires the `accountAdmin` role. + parameters: + - name: appID + description: ID of the app to get. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" responses: "200": - description: Successfully generated description. + description: Successful response. content: application/json: schema: - type: object - required: - - description - properties: - description: - type: string - description: The AI-generated description for the asset. - examples: - - "A powerful rotating column of air extending from a thunderstorm to the ground, characterized by its distinctive funnel shape and destructive force." - "400": - description: Invalid request parameters. + $ref: "#/components/schemas/AccountApp" "401": $ref: "#/components/responses/Unauthorized" "403": - description: Insufficient permissions or quota to perform this operation. - headers: - Retry-After: - description: Seconds to wait before retrying after hitting the long-window quota limit. - schema: - type: integer - "429": - description: Too many requests to perform this operation. - headers: - Retry-After: - description: Seconds to wait before retrying after hitting the short-window rate limit. - schema: - type: integer - - /aigc/asset-adoptions: - post: - operationId: createAIGCAssetAdoption + description: Insufficient permissions to perform this operation. + "404": + description: Account app not found. + patch: + operationId: updateAdminAccountApp tags: - - AIGC - summary: Adopt an AIGC-generated asset + - Account Admin + summary: Update an account app as admin description: | - Record that an AIGC-generated asset has been adopted. - The backend verifies task ownership and file provenance and may publish the adopted asset to the public library. + Update an account app registered for first-party app SSO. + + Requires the `accountAdmin` role. + parameters: + - name: appID + description: ID of the app to update. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" requestBody: required: true content: application/json: schema: type: object - required: - - taskIds - - asset + minProperties: 1 properties: - taskIds: - description: IDs of relevant AIGC tasks that produced the asset. - type: array - items: - $ref: "#/components/schemas/Model/properties/id" - examples: - - - "1" - - "2" - minItems: 1 - maxItems: 100 - asset: - description: Asset data. - type: object - required: - - displayName - - type - - description - - extraSettings - - filesHash - - files - properties: - displayName: - $ref: "#/components/schemas/Asset/properties/displayName" - type: - $ref: "#/components/schemas/Asset/properties/type" - description: - allOf: - - $ref: "#/components/schemas/Asset/properties/description" - minLength: 1 - extraSettings: - $ref: "#/components/schemas/Asset/properties/extraSettings" - filesHash: - $ref: "#/components/schemas/Asset/properties/filesHash" - files: - allOf: - - $ref: "#/components/schemas/Asset/properties/files" - minProperties: 1 + displayName: + $ref: "#/components/schemas/AccountApp/properties/displayName" + redirectURIs: + $ref: "#/components/schemas/AccountApp/properties/redirectURIs" + allowedOrigins: + $ref: "#/components/schemas/AccountApp/properties/allowedOrigins" responses: - "204": - description: Asset adoption recorded. - "400": - description: Invalid parameters. + "200": + description: Account app updated. + content: + application/json: + schema: + $ref: "#/components/schemas/AccountApp" "401": $ref: "#/components/responses/Unauthorized" "403": - description: Insufficient permissions for given tasks. + description: Insufficient permissions to perform this operation. + "404": + description: Account app not found. - /aigc/asset-settings-enrichments: - post: - operationId: enrichAssetSettings + /admin/account/apps/{appID}/status: + put: + operationId: updateAdminAccountAppStatus tags: - - AIGC - summary: Enrich asset settings + - Account Admin + summary: Update an account app status as admin description: | - Use AI to enrich partial asset settings with detailed descriptions and parameters. - - Quota and rate limits: + Enable or disable an account app. - - Each request consumes 1 quota from the authenticated user's AIGC allowance. - - Per-user short-window rate limits apply. Hitting them returns 429 with `Retry-After`. + Requires the `accountAdmin` role. + parameters: + - name: appID + description: ID of the app whose status is updated. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" requestBody: required: true content: @@ -3309,123 +4893,82 @@ paths: schema: type: object required: - - assetType - - input - allOf: - - if: - required: - - assetType - properties: - assetType: - const: sprite - then: - properties: - settings: - $ref: "#/components/schemas/AIGCSpriteSettings" - - if: - required: - - assetType - properties: - assetType: - const: costume - then: - properties: - settings: - $ref: "#/components/schemas/AIGCCostumeSettings" - - if: - required: - - assetType - properties: - assetType: - const: animation - then: - properties: - settings: - $ref: "#/components/schemas/AIGCAnimationSettings" - - if: - required: - - assetType - properties: - assetType: - const: backdrop - then: - properties: - settings: - $ref: "#/components/schemas/AIGCBackdropSettings" + - status properties: - assetType: - description: Type of asset to enrich settings for. - type: string - enum: - - sprite - - costume - - animation - - backdrop - input: - description: User's text description of the asset. - type: string - minLength: 1 - examples: - - A fire demon character with glowing ember cracks - settings: - description: Optional partial settings to be enriched. Structure depends on assetType. - type: object - spriteSettings: - description: Optional existing sprite settings to provide context for costume or animation enrichment. - $ref: "#/components/schemas/AIGCSpriteSettings" - projectSettings: - description: Optional project settings to provide context for enrichment. - $ref: "#/components/schemas/AIGCProjectSettings" - lang: - description: | - Language the AI should use for all generated text. - When provided, the output strictly follows this language regardless of the input language. - When omitted, the AI infers language from the user input. - type: string - enum: - - en - - zh + status: + $ref: "#/components/schemas/AccountApp/properties/status" responses: "200": - description: Successfully enriched settings. + description: Account app status updated. content: application/json: schema: - anyOf: - - $ref: "#/components/schemas/AIGCSpriteSettings" - - $ref: "#/components/schemas/AIGCCostumeSettings" - - $ref: "#/components/schemas/AIGCAnimationSettings" - - $ref: "#/components/schemas/AIGCBackdropSettings" + $ref: "#/components/schemas/AccountApp" "401": $ref: "#/components/responses/Unauthorized" "403": - description: Insufficient permissions or quota. - headers: - Retry-After: - description: Seconds to wait before retrying after hitting the long-window quota limit. - schema: - type: integer - "429": - description: Too many requests. - headers: - Retry-After: - description: Seconds to wait before retrying after hitting the short-window rate limit. - schema: - type: integer + description: Insufficient permissions to perform this operation. + "404": + description: Account app not found. - /aigc/sprite-content-settings: + /admin/account/apps/{appID}/secrets: + get: + operationId: listAdminAccountAppSecrets + tags: + - Account Admin + summary: List account app secrets as admin + description: | + Retrieve a list of secrets configured for a confidential account app. + + Requires the `accountAdmin` role. + parameters: + - name: appID + description: ID of the app whose secrets are listed. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + - $ref: "#/components/parameters/PageIndex" + - $ref: "#/components/parameters/PageSize" + responses: + "200": + description: Successful response. + content: + application/json: + schema: + type: object + required: + - total + - data + properties: + total: + $ref: "#/components/schemas/ByPage/properties/total" + data: + type: array + items: + $ref: "#/components/schemas/AccountAppSecret" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. + "404": + description: Account app not found. post: - operationId: generateSpriteContentSettings + operationId: createAdminAccountAppSecret tags: - - AIGC - summary: Generate sprite content settings + - Account Admin + summary: Create an account app secret as admin description: | - Generate costume and animation settings for a sprite based on its settings. + Create a secret for a confidential account app. The secret value is only returned once. - Quota and rate limits: - - - Each request consumes 1 quota from the authenticated user's AIGC allowance. - - Per-user short-window rate limits apply. Hitting them returns 429 with `Retry-After`. + Requires the `accountAdmin` role. + parameters: + - name: appID + description: ID of the app whose secret is created. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" requestBody: required: true content: @@ -3433,189 +4976,210 @@ paths: schema: type: object required: - - settings + - name properties: - settings: - $ref: "#/components/schemas/AIGCSpriteSettings" - lang: - description: | - Language the AI should use for all generated text. - When provided, the output strictly follows this language regardless of the input language. - When omitted, the AI infers language from the user input. - type: string - enum: - - en - - zh + name: + $ref: "#/components/schemas/AccountAppSecret/properties/name" responses: - "200": - description: Successfully generated content settings. + "201": + description: Account app secret created. content: application/json: schema: - type: object - required: - - costumes - - animations - - animationBindings - properties: - costumes: - description: Settings for costumes to generate. - type: array - items: - $ref: "#/components/schemas/AIGCCostumeSettings" - animations: - description: Settings for animations to generate. - type: array - items: - $ref: "#/components/schemas/AIGCAnimationSettings" - animationBindings: - description: | - Recommended mapping from sprite state enum values to animation names. - Keys are sprite state enum values; values are animation name strings that should match the `name` - field from the animations array, omitted if no animation is bound to that state. - type: object - additionalProperties: false - properties: - default: - type: string - step: - type: string - die: - type: string - example: - default: "idle" - step: "walk" + $ref: "#/components/schemas/CreatedAccountAppSecret" "401": $ref: "#/components/responses/Unauthorized" "403": - description: Insufficient permissions or quota. - headers: - Retry-After: - description: Seconds to wait before retrying after hitting the long-window quota limit. - schema: - type: integer - "429": - description: Too many requests. - headers: - Retry-After: - description: Seconds to wait before retrying after hitting the short-window rate limit. - schema: - type: integer + description: Insufficient permissions to perform this operation. + "404": + description: Account app not found. - /upload-sessions: - post: - operationId: createUploadSession + /admin/account/apps/{appID}/secrets/{secretID}: + delete: + operationId: deleteAdminAccountAppSecret tags: - - Files - summary: Create an upload session - description: Create short-lived upload credentials and configuration for file uploads. + - Account Admin + summary: Delete an account app secret as admin + description: | + Delete a secret from a confidential account app. + + Requires the `accountAdmin` role. + parameters: + - name: appID + description: ID of the app whose secret is deleted. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + - name: secretID + description: ID of the secret to delete. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" responses: - "201": - description: Upload session created. + "204": + description: Account app secret deleted. + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. + "404": + description: Account app or secret not found. + + /admin/authorization/users/{userID}: + get: + operationId: getAdminUserAuthorization + tags: + - Authorization Admin + summary: Get authorization for a user as admin + description: | + Retrieve authorization inputs and derived capabilities for a user. + + Requires the `authorizationAdmin` role. + parameters: + - name: userID + description: ID of the user whose authorization is retrieved. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" + responses: + "200": + description: Successful response. content: application/json: schema: - $ref: "#/components/schemas/UpInfo" + $ref: "#/components/schemas/UserAuthorization" "401": $ref: "#/components/responses/Unauthorized" - - /file-url-signatures: - post: - operationId: createFileURLSignatures + "403": + description: Insufficient permissions to perform this operation. + "404": + description: User authorization not found. + patch: + operationId: updateAdminUserAuthorization tags: - - Files - summary: Generate signed URLs for files - description: Create signed web URLs for the specified files to allow secure access. - security: [] + - Authorization Admin + summary: Update authorization for a user as admin + description: | + Update authorization inputs for a user. + + Requires the `authorizationAdmin` role. + parameters: + - name: userID + description: ID of the user whose authorization is updated. + in: path + required: true + schema: + $ref: "#/components/schemas/Model/properties/id" requestBody: required: true content: application/json: schema: type: object - required: - - objects + minProperties: 1 properties: - objects: - description: List of universal URLs of the objects. - type: array - items: - type: string - minLength: 1 - format: uri - pattern: "^kodo://[^/?#@:]+/[^?#]+$" - examples: - - - kodo://xbuilder-usercontent-test/files/FjgMMuSaAsRWx1t7UAQnQ5r4YsAe-195 + roles: + $ref: "#/components/schemas/UserAuthorization/properties/roles" + plan: + $ref: "#/components/schemas/UserAuthorization/properties/plan" responses: "200": - description: Successfully generated signed URLs for the files. + description: User authorization updated. content: application/json: schema: - type: object - required: - - objectUrls - properties: - objectUrls: - description: Map from universal URLs to signed web URLs for the objects. - type: object - propertyNames: - type: string - format: uri - pattern: "^kodo://[^/?#@:]+/[^?#]+$" - additionalProperties: - type: string - format: uri - examples: - - kodo://xbuilder-usercontent-test/files/FjgMMuSaAsRWx1t7UAQnQ5r4YsAe-195: https://xbuilder-usercontent-test.gopluscdn.com/files/FjgMMuSaAsRWx1t7UAQnQ5r4YsAe-195?e=1727658000&token=t_6AOOkCdDu4m7fPleblcK0gMBZfbGQzeEIVt5Au:EMJdLqcCWrqQ5pRd01diOv7nhQw= + $ref: "#/components/schemas/UserAuthorization" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. + "404": + description: User authorization not found. - /project-conversions: - post: - operationId: convertProject + /admin/audit-logs: + get: + operationId: listAdminAuditLogs tags: - - Files - summary: Convert a project file + - Admin Audit + summary: List audit logs as admin description: | - Convert a Scratch format file to XBP (XBuilder Project) format. + Retrieve admin audit logs. - This endpoint accepts a Scratch project file, version 2 or 3, and converts it to the XBP format used by the - XBuilder platform. - security: - - {} - - bearerAuth: [] - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - required: - - file - properties: - file: - description: Scratch project file, such as .sb2, .sb3, or project zip. Form field name must be `file`. Maximum size is 64 MiB. - type: string - format: binary - maxLength: 67108864 + Requires admin permissions. Backend RBAC determines which audit events are visible to the current admin user. + parameters: + - name: createdAfter + description: Filter audit logs created after this timestamp. + in: query + schema: + type: string + format: date-time + - name: createdBefore + description: Filter audit logs created before this timestamp. + in: query + schema: + type: string + format: date-time + - name: orderBy + description: Field by which to order the results. + in: query + schema: + type: string + enum: + - createdAt + default: createdAt + examples: + - createdAt + - $ref: "#/components/parameters/SortOrder" + - $ref: "#/components/parameters/PageIndex" + - $ref: "#/components/parameters/PageSize" responses: "200": - description: Conversion successful. Returns a zip archive containing the XBP project file. + description: Successful response. content: - application/zip: + application/json: schema: - type: string - format: binary - examples: - xbpFile: - summary: Example XBP zip file + type: object + required: + - total + - data + properties: + total: + $ref: "#/components/schemas/ByPage/properties/total" + data: + type: array + items: + $ref: "#/components/schemas/AuditLog" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: Insufficient permissions to perform this operation. components: securitySchemes: bearerAuth: type: http scheme: bearer - bearerFormat: JWT - description: Access token, in the format `Bearer {token}`. + bearerFormat: Opaque + description: | + Opaque access token, in the format `Bearer {token}`. + + For XBuilder Account OAuth flows, app-scoped access tokens are issued by `/account/oauth/token` and can be + validated with `/account/oauth/introspect`. + accountSessionCookie: + type: apiKey + in: cookie + name: __Host-xbuilder-account-session + description: | + Account session cookie used by Account Web. The cookie must be `HttpOnly`, `Secure`, `SameSite=Lax`, use + `Path=/`, and omit `Domain`. Cookie-authenticated mutation endpoints must validate Origin or use equivalent CSRF + protection. + oauthClientSecretBasic: + type: http + scheme: basic + description: OAuth client authentication using client secret basic. responses: MovedPermanently: @@ -3648,9 +5212,47 @@ components: content: application/json: schema: - $ref: "#/components/schemas/MovedResourceError" - Unauthorized: - description: Unauthorized. Authentication required. + $ref: "#/components/schemas/MovedResourceError" + Unauthorized: + description: Unauthorized. Authentication required. + OAuthInvalidBearerToken: + description: OAuth bearer token authentication failed. + headers: + WWW-Authenticate: + description: Bearer token authentication challenge. + schema: + type: string + examples: + - Bearer realm="XBuilder Account" + - Bearer realm="XBuilder Account", error="invalid_token" + OAuthError: + description: OAuth error response. + content: + application/json: + schema: + $ref: "#/components/schemas/OAuthError" + OAuthInvalidClient: + description: OAuth client authentication failed. + headers: + WWW-Authenticate: + description: Client authentication challenge. + schema: + type: string + examples: + - Basic realm="XBuilder Account" + content: + application/json: + schema: + $ref: "#/components/schemas/OAuthError" + OAuthInsufficientScope: + description: OAuth bearer token does not have the required scope. + headers: + WWW-Authenticate: + description: Bearer token insufficient scope challenge. + schema: + type: string + examples: + - Bearer realm="XBuilder Account", error="insufficient_scope", scope="account:user:read" schemas: Model: @@ -3662,11 +5264,11 @@ components: - updatedAt properties: id: - description: Unique positive identifier as a canonical decimal string. + description: Unique positive int64 identifier serialized as a canonical decimal string. type: string minLength: 1 maxLength: 19 - pattern: '^([1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-7])$' + pattern: "^[1-9][0-9]{0,18}$" examples: - "1" createdAt: @@ -3761,16 +5363,368 @@ components: - id - createdAt - updatedAt - - username + - username + - displayName + - avatar + - description + - plan + - followerCount + - followingCount + - projectCount + - publicProjectCount + - likedProjectCount + properties: + id: + $ref: "#/components/schemas/Model/properties/id" + createdAt: + $ref: "#/components/schemas/Model/properties/createdAt" + updatedAt: + $ref: "#/components/schemas/Model/properties/updatedAt" + username: + description: Unique username of the user. + type: string + minLength: 1 + maxLength: 100 + pattern: "^[A-Za-z0-9_-]{1,100}$" + examples: + - john + displayName: + description: Display name of the user. + type: string + minLength: 1 + maxLength: 100 + examples: + - John Doe + avatar: + description: Public URL or universal URL of the user's avatar image. + type: string + format: uri + examples: + - https://avatars.githubusercontent.com/u/10137?v=4 + description: + description: Brief bio or description of the user. + type: string + examples: + - Two giants live in Britain's land... + plan: + description: Subscription plan of the user. + type: string + enum: + - free + - plus + examples: + - free + followerCount: + description: Number of followers the user has. + type: integer + format: int64 + minimum: 0 + examples: + - 1 + followingCount: + description: Number of users the user is following. + type: integer + format: int64 + minimum: 0 + examples: + - 1 + projectCount: + description: Total number of projects created by the user. + type: integer + format: int64 + minimum: 0 + examples: + - 1 + publicProjectCount: + description: Number of public projects created by the user. + type: integer + format: int64 + minimum: 0 + examples: + - 1 + likedProjectCount: + description: Number of projects liked by the user. + type: integer + format: int64 + minimum: 0 + examples: + - 1 + capabilities: + description: | + Capabilities of the user. + + **This field is only present for the authenticated user's own profile.** + $ref: "#/components/schemas/UserCapabilities" + + AuthenticatedUser: + description: Authenticated user account information. + allOf: + - $ref: "#/components/schemas/User" + - type: object + required: + - capabilities + + UserCapabilities: + type: object + description: User permission capabilities. + required: + - canManageAssets + - canManageCourses + - canUsePremiumLLM + properties: + canManageAssets: + description: Whether the user can manage asset library. + type: boolean + examples: + - true + canManageCourses: + description: Whether the user can manage courses and course series. + type: boolean + examples: + - true + canUsePremiumLLM: + description: Whether the user can access premium LLM models. + type: boolean + examples: + - true + + AccountUser: + description: User fields exposed by XBuilder Account. + type: object + required: + - id + - createdAt + - updatedAt + - username + - displayName + - avatar + properties: + id: + $ref: "#/components/schemas/Model/properties/id" + createdAt: + $ref: "#/components/schemas/Model/properties/createdAt" + updatedAt: + $ref: "#/components/schemas/Model/properties/updatedAt" + username: + $ref: "#/components/schemas/User/properties/username" + displayName: + $ref: "#/components/schemas/User/properties/displayName" + avatar: + $ref: "#/components/schemas/User/properties/avatar" + + AccountIdentityProvider: + type: object + description: Identity provider available for hosted sign-in. + required: + - name + - displayName + - enabled + properties: + name: + description: Identity provider name. + type: string + enum: + - wechat + - qq + - github + - apple + - google + - x + examples: + - github + displayName: + description: Display name of the identity provider. + type: string + examples: + - GitHub + enabled: + description: Whether the identity provider is currently available for hosted sign-in. + type: boolean + examples: + - true + + AccountUserIdentity: + type: object + description: Third-party identity linked to a user. + required: + - id + - createdAt + - updatedAt + - provider + - subject + properties: + id: + $ref: "#/components/schemas/Model/properties/id" + createdAt: + $ref: "#/components/schemas/Model/properties/createdAt" + updatedAt: + $ref: "#/components/schemas/Model/properties/updatedAt" + provider: + $ref: "#/components/schemas/AccountIdentityProvider/properties/name" + subject: + description: Stable provider subject. + type: string + minLength: 1 + subjectNamespace: + description: Provider subject namespace when needed for provider-scoped identifiers. + type: + - string + - "null" + displayName: + description: Provider display name retained for display and troubleshooting. + type: + - string + - "null" + avatar: + description: Provider avatar URL retained for display and troubleshooting. + type: + - string + - "null" + format: uri + + AccountSession: + type: object + description: Account session used by hosted sign-in and SSO continuity. + required: + - id + - createdAt + - updatedAt + - lastUsedAt + - expiresAt + - current + properties: + id: + $ref: "#/components/schemas/Model/properties/id" + createdAt: + $ref: "#/components/schemas/Model/properties/createdAt" + updatedAt: + $ref: "#/components/schemas/Model/properties/updatedAt" + lastUsedAt: + description: Timestamp when the account session was last used. + type: string + format: date-time + examples: + - 2006-01-02T15:04:05+07:00 + expiresAt: + description: Expiration timestamp. + type: string + format: date-time + examples: + - 2006-01-02T15:04:05+07:00 + current: + description: Whether this is the current account session. + type: boolean + examples: + - true + userAgent: + description: User agent associated with the account session. + type: + - string + - "null" + ipAddress: + description: IP address associated with the account session. + type: + - string + - "null" + + CreateAccountSessionRequest: + description: Request to create an account session for hosted sign-in. + oneOf: + - $ref: "#/components/schemas/CreatePasswordAccountSessionRequest" + - $ref: "#/components/schemas/CreateProviderCodeAccountSessionRequest" + discriminator: + propertyName: method + mapping: + password: "#/components/schemas/CreatePasswordAccountSessionRequest" + providerCode: "#/components/schemas/CreateProviderCodeAccountSessionRequest" + + CreatePasswordAccountSessionRequest: + type: object + description: Request to create an account session with an administrator-managed password. + required: + - method + - username + - password + properties: + method: + description: Account session creation method. + type: string + enum: + - password + username: + description: Username for password sign-in. + $ref: "#/components/schemas/AccountUser/properties/username" + password: + description: Administrator-managed password. + type: string + minLength: 1 + maxLength: 128 + + CreateProviderCodeAccountSessionRequest: + type: object + description: Request to create an account session with a provider credential handoff code. + required: + - method + - provider + - providerCode + properties: + method: + description: Account session creation method. + type: string + enum: + - providerCode + provider: + description: Identity provider used for provider credential handoff. + $ref: "#/components/schemas/AccountIdentityProvider/properties/name" + providerCode: + description: Short-lived provider code to consume. + type: string + minLength: 1 + + CurrentAccountSession: + type: object + description: Current account session. + required: + - id + - createdAt + - updatedAt + - lastUsedAt + - expiresAt + - current + - user + properties: + id: + $ref: "#/components/schemas/AccountSession/properties/id" + createdAt: + $ref: "#/components/schemas/AccountSession/properties/createdAt" + updatedAt: + $ref: "#/components/schemas/AccountSession/properties/updatedAt" + lastUsedAt: + $ref: "#/components/schemas/AccountSession/properties/lastUsedAt" + expiresAt: + $ref: "#/components/schemas/AccountSession/properties/expiresAt" + current: + $ref: "#/components/schemas/AccountSession/properties/current" + user: + $ref: "#/components/schemas/AccountUser" + userAgent: + $ref: "#/components/schemas/AccountSession/properties/userAgent" + ipAddress: + $ref: "#/components/schemas/AccountSession/properties/ipAddress" + + AccountApp: + type: object + description: Registered app for first-party app SSO. + required: + - id + - createdAt + - updatedAt + - name - displayName - - avatar - - description - - plan - - followerCount - - followingCount - - projectCount - - publicProjectCount - - likedProjectCount + - clientType + - status + - redirectURIs + - allowedOrigins properties: id: $ref: "#/components/schemas/Model/properties/id" @@ -3778,113 +5732,431 @@ components: $ref: "#/components/schemas/Model/properties/createdAt" updatedAt: $ref: "#/components/schemas/Model/properties/updatedAt" - username: - description: Unique username of the user. + name: + description: Unique app name. type: string minLength: 1 maxLength: 100 pattern: "^[A-Za-z0-9_-]{1,100}$" examples: - - john + - xbuilder displayName: - description: Display name of the user. + description: Display name of the app. type: string minLength: 1 maxLength: 100 examples: - - John Doe - avatar: - description: Public URL or universal URL of the user's avatar image. + - XBuilder + clientType: + description: OAuth client type of the app. type: string - format: uri + enum: + - public + - confidential examples: - - https://avatars.githubusercontent.com/u/10137?v=4 - description: - description: Brief bio or description of the user. + - public + status: + description: App status. type: string + enum: + - active + - disabled examples: - - Two giants live in Britain's land... - plan: - description: Subscription plan of the user. + - active + redirectURIs: + description: | + Allowed redirect URIs. + + The server must require exact redirect URI matching. Production web redirect URIs must use `https`. + + Native app and loopback exceptions must be explicitly registered and validated by client type. + type: array + minItems: 1 + items: + type: string + format: uri + allowedOrigins: + description: | + Allowed web origins. + + Production web origins must use `https`, must not include path, query, or fragment, and must be matched + exactly by the server. + type: array + items: + type: string + format: uri + + AccountAppSecret: + type: object + description: OAuth client secret metadata for a confidential app. + required: + - id + - createdAt + - name + properties: + id: + $ref: "#/components/schemas/Model/properties/id" + createdAt: + $ref: "#/components/schemas/Model/properties/createdAt" + name: + description: App secret name. type: string - enum: - - free - - plus + minLength: 1 + maxLength: 100 examples: - - free - followerCount: - description: Number of followers the user has. - type: integer - format: int64 - minimum: 0 + - production + + CreatedAccountAppSecret: + description: OAuth client secret created response including the one-time secret value. + allOf: + - $ref: "#/components/schemas/AccountAppSecret" + - type: object + required: + - value + properties: + value: + description: One-time app secret value. + type: string + minLength: 1 + + OAuthClientID: + description: OAuth client ID of the app. + type: string + minLength: 1 + maxLength: 19 + pattern: "^[1-9][0-9]{0,18}$" + examples: + - "1" + + OAuthPKCECodeChallenge: + description: PKCE code challenge. + type: string + minLength: 43 + maxLength: 128 + pattern: "^[A-Za-z0-9._~-]{43,128}$" + + OAuthPKCECodeVerifier: + description: PKCE code verifier. + type: string + minLength: 43 + maxLength: 128 + pattern: "^[A-Za-z0-9._~-]{43,128}$" + + OAuthScope: + description: Account API OAuth scope supported by XBuilder Account. + type: string + enum: + - account:user:read + examples: + - account:user:read + + OAuthPushedAuthorizationResponse: + type: object + description: Pushed authorization request response. + required: + - request_uri + - expires_in + properties: + request_uri: + description: | + Opaque pushed authorization request URI. + + The value is a short-lived, single-use reference and is not a dereferenceable URL. + type: string + format: uri + minLength: 1 examples: - - 1 - followingCount: - description: Number of users the user is following. + - urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY22c + expires_in: + description: Lifetime of the pushed authorization request in seconds. type: integer format: int64 - minimum: 0 + minimum: 1 examples: - - 1 - projectCount: - description: Total number of projects created by the user. + - 600 + + CreateOAuthTokenRequest: + description: Request to create or refresh Account-issued app-scoped OAuth tokens. + oneOf: + - $ref: "#/components/schemas/CreateAuthorizationCodeOAuthTokenRequest" + - $ref: "#/components/schemas/CreateRefreshTokenOAuthTokenRequest" + discriminator: + propertyName: grant_type + mapping: + authorization_code: "#/components/schemas/CreateAuthorizationCodeOAuthTokenRequest" + refresh_token: "#/components/schemas/CreateRefreshTokenOAuthTokenRequest" + + CreateAuthorizationCodeOAuthTokenRequest: + type: object + description: Request to exchange an authorization code for Account-issued app-scoped OAuth tokens. + required: + - grant_type + - code + - redirect_uri + - code_verifier + properties: + grant_type: + description: OAuth grant type. + type: string + enum: + - authorization_code + code: + description: Authorization code. + type: string + minLength: 1 + redirect_uri: + description: Redirect URI used in the authorization request. + type: string + format: uri + code_verifier: + $ref: "#/components/schemas/OAuthPKCECodeVerifier" + client_id: + description: | + OAuth client ID of the app. + + **Required when the client does not authenticate with the OAuth client authentication method + `client_secret_basic`.** + allOf: + - $ref: "#/components/schemas/OAuthClientID" + + CreateRefreshTokenOAuthTokenRequest: + type: object + description: Request to refresh Account-issued app-scoped OAuth tokens. + required: + - grant_type + - refresh_token + properties: + grant_type: + description: OAuth grant type. + type: string + enum: + - refresh_token + refresh_token: + description: Refresh token issued by XBuilder Account. + type: string + minLength: 1 + scope: + description: OAuth scope requested for the refreshed access token. + allOf: + - $ref: "#/components/schemas/OAuthScope" + client_id: + description: | + OAuth client ID of the app. + + **Required when the client does not authenticate with the OAuth client authentication method + `client_secret_basic`.** + allOf: + - $ref: "#/components/schemas/OAuthClientID" + + OAuthToken: + type: object + description: OAuth token response. + required: + - access_token + - token_type + - expires_in + - scope + properties: + access_token: + description: Account-issued app-scoped OAuth access token. + type: string + minLength: 1 + token_type: + description: OAuth token type. + type: string + enum: + - Bearer + expires_in: + description: Lifetime of the access token in seconds. type: integer format: int64 - minimum: 0 + minimum: 1 examples: - - 1 - publicProjectCount: - description: Number of public projects created by the user. + - 300 + refresh_token: + description: Refresh token issued by XBuilder Account. + type: string + minLength: 1 + scope: + description: Granted OAuth scope. + allOf: + - $ref: "#/components/schemas/OAuthScope" + + OAuthIntrospection: + type: object + description: OAuth token introspection response. + required: + - active + properties: + active: + description: Whether the token is active. + type: boolean + examples: + - true + sub: + description: Stable XBuilder user ID used as the OAuth subject. + $ref: "#/components/schemas/Model/properties/id" + client_id: + description: OAuth client ID of the app the token was issued to. + $ref: "#/components/schemas/OAuthClientID" + scope: + description: OAuth scope associated with the token. + allOf: + - $ref: "#/components/schemas/OAuthScope" + exp: + description: Expiration time as a Unix timestamp. type: integer format: int64 minimum: 0 - examples: - - 1 - likedProjectCount: - description: Number of projects liked by the user. + iat: + description: Issued-at time as a Unix timestamp. type: integer format: int64 minimum: 0 + + RevokeOAuthTokenRequest: + description: Request to revoke an Account-issued OAuth token. + oneOf: + - $ref: "#/components/schemas/RevokeOAuthTokenWithClientAuthRequest" + - $ref: "#/components/schemas/RevokeOAuthTokenWithClientIDRequest" + + RevokeOAuthTokenWithClientAuthRequest: + type: object + description: Token revocation request authenticated with OAuth client authentication. + required: + - token + not: + required: + - client_id + properties: + token: + description: OAuth token to revoke. + type: string + minLength: 1 + token_type_hint: + description: | + Hint about the type of token submitted for revocation. + + Known values include `access_token` and `refresh_token`. + type: string + minLength: 1 examples: - - 1 - capabilities: + - access_token + + RevokeOAuthTokenWithClientIDRequest: + type: object + description: Token revocation request from a public client. + required: + - token + - client_id + properties: + token: + description: OAuth token to revoke. + type: string + minLength: 1 + token_type_hint: description: | - Capabilities of the user. + Hint about the type of token submitted for revocation. - **This field is only present for the authenticated user's own profile.** - $ref: "#/components/schemas/UserCapabilities" + Known values include `access_token` and `refresh_token`. + type: string + minLength: 1 + examples: + - access_token + client_id: + description: OAuth client ID of the app. + $ref: "#/components/schemas/OAuthClientID" - AuthenticatedUser: - description: Authenticated user account information. - allOf: - - $ref: "#/components/schemas/User" - - type: object - required: - - capabilities + OAuthError: + type: object + description: OAuth error response body. + required: + - error + properties: + error: + description: OAuth error code. + type: string + minLength: 1 + error_description: + description: Human-readable ASCII description of the error. + type: string + minLength: 1 + error_uri: + description: URI identifying a human-readable web page with information about the error. + type: string + format: uri-reference - UserCapabilities: + AuditLog: type: object - description: User permission capabilities. + description: Admin audit log entry. required: - - canManageAssets - - canManageCourses - - canUsePremiumLLM + - id + - createdAt + - action + - resourceType + - resourceID properties: - canManageAssets: - description: Whether the user can manage asset library. - type: boolean + id: + $ref: "#/components/schemas/Model/properties/id" + createdAt: + $ref: "#/components/schemas/Model/properties/createdAt" + action: + description: Audit action name. + type: string + minLength: 1 examples: - - true - canManageCourses: - description: Whether the user can manage courses and course series. - type: boolean + - account.user.password.set + actor: + description: Username of the user who performed the action at the time of the audit event. + oneOf: + - $ref: "#/components/schemas/User/properties/username" + - type: "null" + resourceType: + description: Type of the audited resource. + type: string + minLength: 1 examples: - - true - canUsePremiumLLM: - description: Whether the user can access premium LLM models. - type: boolean + - account.user + resourceID: + description: ID of the audited resource. + $ref: "#/components/schemas/Model/properties/id" + metadata: + description: | + Additional audit metadata. + + Metadata must not contain passwords, tokens, client secrets, or other secrets. + type: object + additionalProperties: true + + UserAuthorization: + type: object + description: XBuilder Authorization inputs and derived capabilities for a user. + required: + - userID + - roles + - plan + - capabilities + properties: + userID: + description: ID of the user. + $ref: "#/components/schemas/Model/properties/id" + roles: + description: Roles assigned to the user. + type: array + items: + type: string + minLength: 1 examples: - - true + - - assetAdmin + - courseAdmin + plan: + $ref: "#/components/schemas/User/properties/plan" + capabilities: + $ref: "#/components/schemas/UserCapabilities" + quotaPolicies: + description: Derived quota policies for XBuilder Authorization decisions. + type: object + additionalProperties: true Project: type: object