openapi: 3.0.3 info: title: Galaxy User Service Internal REST API version: v1 description: | This specification documents the planned trusted internal REST contract of `galaxy/user`. Scope: - auth-facing user resolution, ensure, existence, and subject blocking - gateway-facing authenticated account reads and self-service mutations - lobby-facing eligibility snapshots - geo-facing declared-country synchronization - admin/internal reads, filtered listing, and explicit mutation commands This specification is internal REST only. It intentionally does not describe public edge transport, gateway gRPC, or any future asynchronous event payloads. servers: - url: http://localhost:8091 description: Example local internal listener for User Service. tags: - name: AuthIntegration description: Trusted auth-facing user ownership and block-policy endpoints. - name: MyAccount description: Gateway-facing authenticated account queries and self-service mutations. - name: LobbyIntegration description: Trusted lobby-facing synchronous eligibility reads. - name: GeoIntegration description: Trusted geo-facing declared-country synchronization. - name: AdminUsers description: Trusted administrative lookup, listing, and explicit mutation commands. paths: /api/v1/internal/user-resolutions/by-email: post: tags: - AuthIntegration operationId: resolveUserByEmail summary: Resolve one e-mail address without creating a user requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UserResolutionByEmailRequest" responses: "200": description: Current coarse user-resolution state for the e-mail subject. content: application/json: schema: $ref: "#/components/schemas/UserResolutionByEmailResponse" "400": $ref: "#/components/responses/InvalidRequestError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/users/{user_id}/exists: get: tags: - AuthIntegration operationId: userExistsByID summary: Check whether a stable user identifier exists parameters: - $ref: "#/components/parameters/UserIDPath" responses: "200": description: Existence check result for the supplied `user_id`. content: application/json: schema: $ref: "#/components/schemas/UserExistsResponse" "400": $ref: "#/components/responses/InvalidRequestError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/users/ensure-by-email: post: tags: - AuthIntegration operationId: ensureUserByEmail summary: Resolve, create, or block one e-mail subject description: | Returns an existing user for `email`, creates a new regular platform user when registration is allowed, or returns a blocked outcome when policy denies the flow. `registration_context` is create-only. Implementations must ignore it for existing users and must not overwrite settings of an already existing account. requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/EnsureByEmailRequest" responses: "200": description: Ensure-user outcome for the supplied `email`. content: application/json: schema: $ref: "#/components/schemas/EnsureByEmailResponse" "400": $ref: "#/components/responses/InvalidRequestError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/users/{user_id}/block: post: tags: - AuthIntegration operationId: blockUserByID summary: Block one user by stable user identifier parameters: - $ref: "#/components/parameters/UserIDPath" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/BlockUserByIDRequest" responses: "200": description: The block mutation applied or the subject was already blocked. content: application/json: schema: $ref: "#/components/schemas/BlockMutationResponse" "400": $ref: "#/components/responses/InvalidRequestError" "404": $ref: "#/components/responses/SubjectNotFoundError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/user-blocks/by-email: post: tags: - AuthIntegration operationId: blockUserByEmail summary: Block one e-mail subject even when no user exists yet requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/BlockUserByEmailRequest" responses: "200": description: The block mutation applied or the subject was already blocked. content: application/json: schema: $ref: "#/components/schemas/BlockMutationResponse" "400": $ref: "#/components/responses/InvalidRequestError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/users/{user_id}/account: get: tags: - MyAccount operationId: getMyAccount summary: Read one authenticated regular-user account aggregate parameters: - $ref: "#/components/parameters/UserIDPath" responses: "200": description: Read-optimized account aggregate for the supplied `user_id`. content: application/json: schema: $ref: "#/components/schemas/GetMyAccountResponse" "400": $ref: "#/components/responses/InvalidRequestError" "404": $ref: "#/components/responses/SubjectNotFoundError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/users/{user_id}/profile: post: tags: - MyAccount operationId: updateMyProfile summary: Update self-service profile fields parameters: - $ref: "#/components/parameters/UserIDPath" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateMyProfileRequest" responses: "200": description: Updated account aggregate after the profile mutation commits. content: application/json: schema: $ref: "#/components/schemas/GetMyAccountResponse" "400": $ref: "#/components/responses/InvalidRequestError" "404": $ref: "#/components/responses/SubjectNotFoundError" "409": $ref: "#/components/responses/ConflictError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/users/{user_id}/settings: post: tags: - MyAccount operationId: updateMySettings summary: Update self-service settings fields parameters: - $ref: "#/components/parameters/UserIDPath" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateMySettingsRequest" responses: "200": description: Updated account aggregate after the settings mutation commits. content: application/json: schema: $ref: "#/components/schemas/GetMyAccountResponse" "400": $ref: "#/components/responses/InvalidRequestError" "404": $ref: "#/components/responses/SubjectNotFoundError" "409": $ref: "#/components/responses/ConflictError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/users/{user_id}/eligibility: get: tags: - LobbyIntegration operationId: getUserEligibility summary: Read one synchronous lobby-facing eligibility snapshot description: | Returns a read-optimized snapshot for lobby decisions. Unknown users are represented as `exists=false` instead of `404`. parameters: - $ref: "#/components/parameters/UserIDPath" responses: "200": description: Eligibility snapshot for the supplied `user_id`. content: application/json: schema: $ref: "#/components/schemas/UserEligibilityResponse" "400": $ref: "#/components/responses/InvalidRequestError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/users/{user_id}/declared-country/sync: post: tags: - GeoIntegration operationId: syncDeclaredCountry summary: Synchronize the current effective declared country parameters: - $ref: "#/components/parameters/UserIDPath" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/SyncDeclaredCountryRequest" responses: "200": description: Declared-country synchronization applied successfully. content: application/json: schema: $ref: "#/components/schemas/DeclaredCountrySyncResponse" "400": $ref: "#/components/responses/InvalidRequestError" "404": $ref: "#/components/responses/SubjectNotFoundError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/users/{user_id}: get: tags: - AdminUsers operationId: getUserByID summary: Read one user by stable user identifier parameters: - $ref: "#/components/parameters/UserIDPath" responses: "200": description: Exact user lookup result for the supplied `user_id`. content: application/json: schema: $ref: "#/components/schemas/UserLookupResponse" "400": $ref: "#/components/responses/InvalidRequestError" "404": $ref: "#/components/responses/SubjectNotFoundError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/user-lookups/by-email: post: tags: - AdminUsers operationId: getUserByEmail summary: Read one user by normalized e-mail requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UserLookupByEmailRequest" responses: "200": description: Exact user lookup result for the supplied `email`. content: application/json: schema: $ref: "#/components/schemas/UserLookupResponse" "400": $ref: "#/components/responses/InvalidRequestError" "404": $ref: "#/components/responses/SubjectNotFoundError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/user-lookups/by-race-name: post: tags: - AdminUsers operationId: getUserByRaceName summary: Read one user by exact race name requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UserLookupByRaceNameRequest" responses: "200": description: Exact user lookup result for the supplied `race_name`. content: application/json: schema: $ref: "#/components/schemas/UserLookupResponse" "400": $ref: "#/components/responses/InvalidRequestError" "404": $ref: "#/components/responses/SubjectNotFoundError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/users: get: tags: - AdminUsers operationId: listUsers summary: List users with deterministic pagination and rich filters parameters: - $ref: "#/components/parameters/PageSize" - $ref: "#/components/parameters/PageToken" - name: paid_state in: query description: Filter by current free or paid state. schema: type: string enum: - free - paid - name: paid_expires_before in: query description: Filter to users whose paid entitlement expires before this RFC 3339 timestamp. schema: type: string format: date-time - name: paid_expires_after in: query description: Filter to users whose paid entitlement expires after this RFC 3339 timestamp. schema: type: string format: date-time - name: declared_country in: query description: Filter by the current effective declared country. schema: $ref: "#/components/schemas/CountryCode" - name: sanction_code in: query description: Filter by one active sanction code. schema: $ref: "#/components/schemas/SanctionCode" - name: limit_code in: query description: Filter by one active limit code. schema: $ref: "#/components/schemas/LimitCode" - name: can_login in: query description: Filter by the derived login eligibility marker. schema: type: boolean - name: can_create_private_game in: query description: Filter by the derived private-game creation eligibility marker. schema: type: boolean - name: can_join_game in: query description: Filter by the derived game-join eligibility marker. schema: type: boolean responses: "200": description: Deterministically ordered page of users. content: application/json: schema: $ref: "#/components/schemas/UserListResponse" "400": $ref: "#/components/responses/InvalidRequestError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/users/{user_id}/entitlements/grant: post: tags: - AdminUsers operationId: grantEntitlement summary: Grant a new entitlement period parameters: - $ref: "#/components/parameters/UserIDPath" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/GrantEntitlementRequest" responses: "200": description: Entitlement grant applied successfully. content: application/json: schema: $ref: "#/components/schemas/EntitlementCommandResponse" "400": $ref: "#/components/responses/InvalidRequestError" "404": $ref: "#/components/responses/SubjectNotFoundError" "409": $ref: "#/components/responses/ConflictError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/users/{user_id}/entitlements/extend: post: tags: - AdminUsers operationId: extendEntitlement summary: Extend the current entitlement period parameters: - $ref: "#/components/parameters/UserIDPath" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/ExtendEntitlementRequest" responses: "200": description: Entitlement extension applied successfully. content: application/json: schema: $ref: "#/components/schemas/EntitlementCommandResponse" "400": $ref: "#/components/responses/InvalidRequestError" "404": $ref: "#/components/responses/SubjectNotFoundError" "409": $ref: "#/components/responses/ConflictError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/users/{user_id}/entitlements/revoke: post: tags: - AdminUsers operationId: revokeEntitlement summary: Revoke the effective paid entitlement parameters: - $ref: "#/components/parameters/UserIDPath" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/RevokeEntitlementRequest" responses: "200": description: Entitlement revocation applied successfully. content: application/json: schema: $ref: "#/components/schemas/EntitlementCommandResponse" "400": $ref: "#/components/responses/InvalidRequestError" "404": $ref: "#/components/responses/SubjectNotFoundError" "409": $ref: "#/components/responses/ConflictError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/users/{user_id}/sanctions/apply: post: tags: - AdminUsers operationId: applySanction summary: Apply one sanction record parameters: - $ref: "#/components/parameters/UserIDPath" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/ApplySanctionRequest" responses: "200": description: Sanction application applied successfully. content: application/json: schema: $ref: "#/components/schemas/SanctionCommandResponse" "400": $ref: "#/components/responses/InvalidRequestError" "404": $ref: "#/components/responses/SubjectNotFoundError" "409": $ref: "#/components/responses/ConflictError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/users/{user_id}/sanctions/remove: post: tags: - AdminUsers operationId: removeSanction summary: Remove one active sanction record parameters: - $ref: "#/components/parameters/UserIDPath" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/RemoveSanctionRequest" responses: "200": description: Sanction removal applied successfully. content: application/json: schema: $ref: "#/components/schemas/SanctionCommandResponse" "400": $ref: "#/components/responses/InvalidRequestError" "404": $ref: "#/components/responses/SubjectNotFoundError" "409": $ref: "#/components/responses/ConflictError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/users/{user_id}/limits/set: post: tags: - AdminUsers operationId: setLimit summary: Set one active user-specific limit record parameters: - $ref: "#/components/parameters/UserIDPath" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/SetLimitRequest" responses: "200": description: User-specific limit set successfully. content: application/json: schema: $ref: "#/components/schemas/LimitCommandResponse" "400": $ref: "#/components/responses/InvalidRequestError" "404": $ref: "#/components/responses/SubjectNotFoundError" "409": $ref: "#/components/responses/ConflictError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" /api/v1/internal/users/{user_id}/limits/remove: post: tags: - AdminUsers operationId: removeLimit summary: Remove one active user-specific limit record parameters: - $ref: "#/components/parameters/UserIDPath" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/RemoveLimitRequest" responses: "200": description: User-specific limit removal applied successfully. content: application/json: schema: $ref: "#/components/schemas/LimitCommandResponse" "400": $ref: "#/components/responses/InvalidRequestError" "404": $ref: "#/components/responses/SubjectNotFoundError" "409": $ref: "#/components/responses/ConflictError" "500": $ref: "#/components/responses/InternalError" "503": $ref: "#/components/responses/ServiceUnavailableError" components: parameters: UserIDPath: name: user_id in: path required: true description: Stable regular-user identifier owned by User Service. schema: $ref: "#/components/schemas/UserID" PageSize: name: page_size in: query description: Maximum number of users returned in one page. schema: type: integer minimum: 1 maximum: 200 default: 50 PageToken: name: page_token in: query description: Opaque deterministic pagination cursor returned by the previous page. schema: type: string schemas: UserID: type: string description: Stable regular-user identifier. minLength: 1 Email: type: string format: email description: Normalized login and contact e-mail address. RaceName: type: string description: | Stored race name preserving the user-selected casing after successful uniqueness checks. minLength: 1 maxLength: 64 LanguageTag: type: string description: BCP 47 language tag. minLength: 1 maxLength: 32 TimeZoneName: type: string description: IANA time zone name. minLength: 1 maxLength: 128 CountryCode: type: string description: ISO 3166-1 alpha-2 country code. pattern: "^[A-Z]{2}$" UserResolutionKind: type: string enum: - existing - creatable - blocked EnsureUserOutcome: type: string enum: - existing - created - blocked BlockUserOutcome: type: string enum: - blocked - already_blocked PlanCode: type: string enum: - free - paid_monthly - paid_yearly - paid_lifetime SanctionCode: type: string enum: - login_block - private_game_create_block - private_game_manage_block - game_join_block - profile_update_block LimitCode: type: string enum: - max_owned_private_games - max_active_private_games - max_pending_public_applications - max_pending_private_join_requests - max_pending_private_invites_sent - max_active_game_memberships ActorRef: type: object additionalProperties: false required: - type properties: type: type: string description: Machine-readable actor type such as `admin`, `service`, or `billing`. id: type: string description: Optional stable actor identifier. RegistrationContext: type: object additionalProperties: false required: - preferred_language - time_zone properties: preferred_language: $ref: "#/components/schemas/LanguageTag" description: | Create-only initial preferred language. During the current rollout phase `Auth / Session Service` sends a temporary `"en"` default until gateway geoip-based language derivation is deployed. time_zone: $ref: "#/components/schemas/TimeZoneName" description: Create-only initial IANA time zone name. UserResolutionByEmailRequest: type: object additionalProperties: false required: - email properties: email: $ref: "#/components/schemas/Email" UserResolutionByEmailResponse: type: object additionalProperties: false required: - kind properties: kind: $ref: "#/components/schemas/UserResolutionKind" user_id: $ref: "#/components/schemas/UserID" block_reason_code: type: string description: Present only for `kind=blocked`. UserExistsResponse: type: object additionalProperties: false required: - exists properties: exists: type: boolean EnsureByEmailRequest: type: object additionalProperties: false required: - email properties: email: $ref: "#/components/schemas/Email" registration_context: $ref: "#/components/schemas/RegistrationContext" EnsureByEmailResponse: type: object additionalProperties: false required: - outcome properties: outcome: $ref: "#/components/schemas/EnsureUserOutcome" user_id: $ref: "#/components/schemas/UserID" block_reason_code: type: string description: Present only for `outcome=blocked`. BlockUserByIDRequest: type: object additionalProperties: false required: - reason_code properties: reason_code: type: string BlockUserByEmailRequest: type: object additionalProperties: false required: - email - reason_code properties: email: $ref: "#/components/schemas/Email" reason_code: type: string BlockMutationResponse: type: object additionalProperties: false required: - outcome properties: outcome: $ref: "#/components/schemas/BlockUserOutcome" user_id: $ref: "#/components/schemas/UserID" EntitlementSnapshot: type: object additionalProperties: false required: - plan_code - is_paid - source - starts_at - updated_at properties: plan_code: $ref: "#/components/schemas/PlanCode" is_paid: type: boolean source: type: string actor: $ref: "#/components/schemas/ActorRef" reason_code: type: string starts_at: type: string format: date-time ends_at: type: string format: date-time updated_at: type: string format: date-time ActiveSanction: type: object additionalProperties: false required: - sanction_code - scope - reason_code - applied_at properties: sanction_code: $ref: "#/components/schemas/SanctionCode" scope: type: string reason_code: type: string actor: $ref: "#/components/schemas/ActorRef" applied_at: type: string format: date-time expires_at: type: string format: date-time ActiveLimit: type: object additionalProperties: false required: - limit_code - value - reason_code - applied_at properties: limit_code: $ref: "#/components/schemas/LimitCode" value: type: integer minimum: 0 reason_code: type: string actor: $ref: "#/components/schemas/ActorRef" applied_at: type: string format: date-time expires_at: type: string format: date-time AccountView: type: object additionalProperties: false required: - user_id - email - race_name - preferred_language - time_zone - entitlement - active_sanctions - active_limits - created_at - updated_at properties: user_id: $ref: "#/components/schemas/UserID" email: $ref: "#/components/schemas/Email" race_name: $ref: "#/components/schemas/RaceName" preferred_language: $ref: "#/components/schemas/LanguageTag" time_zone: $ref: "#/components/schemas/TimeZoneName" declared_country: $ref: "#/components/schemas/CountryCode" entitlement: $ref: "#/components/schemas/EntitlementSnapshot" active_sanctions: type: array items: $ref: "#/components/schemas/ActiveSanction" active_limits: type: array items: $ref: "#/components/schemas/ActiveLimit" created_at: type: string format: date-time updated_at: type: string format: date-time GetMyAccountResponse: type: object additionalProperties: false required: - account properties: account: $ref: "#/components/schemas/AccountView" UpdateMyProfileRequest: type: object additionalProperties: false required: - race_name properties: race_name: $ref: "#/components/schemas/RaceName" UpdateMySettingsRequest: type: object additionalProperties: false required: - preferred_language - time_zone properties: preferred_language: $ref: "#/components/schemas/LanguageTag" time_zone: $ref: "#/components/schemas/TimeZoneName" EligibilityMarkers: type: object additionalProperties: false required: - can_login - can_create_private_game - can_manage_private_game - can_join_game - can_update_profile properties: can_login: type: boolean can_create_private_game: type: boolean can_manage_private_game: type: boolean can_join_game: type: boolean can_update_profile: type: boolean UserEligibilityResponse: type: object additionalProperties: false required: - exists - user_id - markers properties: exists: type: boolean user_id: $ref: "#/components/schemas/UserID" entitlement: $ref: "#/components/schemas/EntitlementSnapshot" active_sanctions: type: array items: $ref: "#/components/schemas/ActiveSanction" effective_limits: type: array items: $ref: "#/components/schemas/ActiveLimit" markers: $ref: "#/components/schemas/EligibilityMarkers" SyncDeclaredCountryRequest: type: object additionalProperties: false required: - declared_country properties: declared_country: $ref: "#/components/schemas/CountryCode" DeclaredCountrySyncResponse: type: object additionalProperties: false required: - user_id - declared_country - updated_at properties: user_id: $ref: "#/components/schemas/UserID" declared_country: $ref: "#/components/schemas/CountryCode" updated_at: type: string format: date-time UserAdminView: allOf: - $ref: "#/components/schemas/AccountView" UserLookupByEmailRequest: type: object additionalProperties: false required: - email properties: email: $ref: "#/components/schemas/Email" UserLookupByRaceNameRequest: type: object additionalProperties: false required: - race_name properties: race_name: $ref: "#/components/schemas/RaceName" UserLookupResponse: type: object additionalProperties: false required: - user properties: user: $ref: "#/components/schemas/UserAdminView" UserListResponse: type: object additionalProperties: false required: - items properties: items: type: array items: $ref: "#/components/schemas/UserAdminView" next_page_token: type: string GrantEntitlementRequest: type: object additionalProperties: false required: - plan_code - source - reason_code - actor - starts_at properties: plan_code: $ref: "#/components/schemas/PlanCode" source: type: string reason_code: type: string actor: $ref: "#/components/schemas/ActorRef" starts_at: type: string format: date-time ends_at: type: string format: date-time ExtendEntitlementRequest: type: object additionalProperties: false required: - source - reason_code - actor - ends_at properties: source: type: string reason_code: type: string actor: $ref: "#/components/schemas/ActorRef" ends_at: type: string format: date-time RevokeEntitlementRequest: type: object additionalProperties: false required: - source - reason_code - actor properties: source: type: string reason_code: type: string actor: $ref: "#/components/schemas/ActorRef" EntitlementCommandResponse: type: object additionalProperties: false required: - user_id - entitlement properties: user_id: $ref: "#/components/schemas/UserID" entitlement: $ref: "#/components/schemas/EntitlementSnapshot" ApplySanctionRequest: type: object additionalProperties: false required: - sanction_code - scope - reason_code - actor - applied_at properties: sanction_code: $ref: "#/components/schemas/SanctionCode" scope: type: string reason_code: type: string actor: $ref: "#/components/schemas/ActorRef" applied_at: type: string format: date-time expires_at: type: string format: date-time RemoveSanctionRequest: type: object additionalProperties: false required: - sanction_code - reason_code - actor properties: sanction_code: $ref: "#/components/schemas/SanctionCode" reason_code: type: string actor: $ref: "#/components/schemas/ActorRef" SanctionCommandResponse: type: object additionalProperties: false required: - user_id - active_sanctions properties: user_id: $ref: "#/components/schemas/UserID" active_sanctions: type: array items: $ref: "#/components/schemas/ActiveSanction" SetLimitRequest: type: object additionalProperties: false required: - limit_code - value - reason_code - actor - applied_at properties: limit_code: $ref: "#/components/schemas/LimitCode" value: type: integer minimum: 0 reason_code: type: string actor: $ref: "#/components/schemas/ActorRef" applied_at: type: string format: date-time expires_at: type: string format: date-time RemoveLimitRequest: type: object additionalProperties: false required: - limit_code - reason_code - actor properties: limit_code: $ref: "#/components/schemas/LimitCode" reason_code: type: string actor: $ref: "#/components/schemas/ActorRef" LimitCommandResponse: type: object additionalProperties: false required: - user_id - active_limits properties: user_id: $ref: "#/components/schemas/UserID" active_limits: type: array items: $ref: "#/components/schemas/ActiveLimit" ErrorResponse: type: object additionalProperties: false required: - error properties: error: $ref: "#/components/schemas/ErrorBody" ErrorBody: type: object additionalProperties: false required: - code - message properties: code: type: string message: type: string responses: InvalidRequestError: description: Request body, path, or query fields are invalid. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalidRequest: value: error: code: invalid_request message: request is invalid SubjectNotFoundError: description: The referenced user or lookup subject does not exist. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: subjectNotFound: value: error: code: subject_not_found message: subject not found ConflictError: description: The requested mutation conflicts with current source-of-truth state. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: conflict: value: error: code: conflict message: request conflicts with current state InternalError: description: Internal User Service error. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: internalError: value: error: code: internal_error message: internal server error ServiceUnavailableError: description: User Service is temporarily unable to serve the request safely. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: unavailable: value: error: code: service_unavailable message: service is unavailable