1645 lines
52 KiB
YAML
1645 lines
52 KiB
YAML
openapi: 3.0.3
|
|
info:
|
|
title: Galaxy User Service Internal REST API
|
|
version: v1
|
|
description: |
|
|
This specification documents the trusted internal REST contract of
|
|
`galaxy/user`.
|
|
|
|
The current runtime is implemented as an internal-only HTTP service backed
|
|
by Redis.
|
|
|
|
Scope:
|
|
- regular-user state only; system-admin identity belongs to future
|
|
`Admin Service`
|
|
- 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 the auxiliary async
|
|
event contracts documented in `README.md` and `docs/flows.md`.
|
|
|
|
The auth-facing paths listed under `AuthIntegration` are already reserved
|
|
by `Auth / Session Service` and their route shapes must remain stable.
|
|
|
|
Current transport rules:
|
|
- request bodies are strict JSON only
|
|
- unknown fields are rejected
|
|
- trailing JSON input is rejected
|
|
- error responses use `{ "error": { "code", "message" } }`
|
|
- stable error codes are `invalid_request`, `conflict`,
|
|
`subject_not_found`, `internal_error`, and `service_unavailable`
|
|
servers:
|
|
- url: http://localhost:8091
|
|
description: Default local internal listener for User Service.
|
|
tags:
|
|
- name: AuthIntegration
|
|
description: Trusted auth-facing user ownership and block-policy endpoints with frozen route shapes reserved by `Auth / Session Service`.
|
|
- 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 required on the current auth-to-user call.
|
|
Its frozen shape is `preferred_language` plus `time_zone`. The
|
|
registration context is create-only. Implementations must ignore it for
|
|
existing users and must not overwrite settings of an already existing
|
|
account.
|
|
|
|
During the current rollout `Auth / Session Service` sends temporary
|
|
`preferred_language="en"` and forwards the public confirm `time_zone`.
|
|
Gateway-side geoip language derivation is a later rollout and is not
|
|
part of the current source-of-truth contract.
|
|
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
|
|
description: |
|
|
Accepts only `display_name`. Validation delegates to
|
|
`pkg/util/string.go:ValidateTypeName`; an empty value is accepted
|
|
and resets any stored display name. `user_name` is immutable and is
|
|
not returned in the request body.
|
|
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
|
|
description: |
|
|
Applies the latest effective declared country chosen by
|
|
`Geo Profile Service`.
|
|
|
|
`declared_country` must be a known uppercase ISO 3166-1 alpha-2
|
|
country code. When the supplied value is already stored on the user
|
|
account, the command is a no-op and returns the existing
|
|
`updated_at` unchanged.
|
|
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 exact-after-trim 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-user-name:
|
|
post:
|
|
tags:
|
|
- AdminUsers
|
|
operationId: getUserByUserName
|
|
summary: Read one user by exact user name
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UserLookupByUserNameRequest"
|
|
responses:
|
|
"200":
|
|
description: Exact user lookup result for the supplied `user_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
|
|
description: |
|
|
Returns full user account aggregates ordered by `created_at desc`, then
|
|
`user_id desc`.
|
|
|
|
All supplied query filters combine with logical `AND`.
|
|
|
|
`page_token` is opaque and bound to the normalized filter set that
|
|
produced it. Malformed or filter-mismatched tokens return
|
|
`400 invalid_request`.
|
|
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
|
|
- name: user_name
|
|
in: query
|
|
description: Filter by exact `user_name`.
|
|
schema:
|
|
$ref: "#/components/schemas/UserName"
|
|
- name: display_name
|
|
in: query
|
|
description: Filter by `display_name`. Combined with `display_name_match`.
|
|
schema:
|
|
$ref: "#/components/schemas/DisplayName"
|
|
- name: display_name_match
|
|
in: query
|
|
description: Match mode for `display_name`; defaults to `exact`.
|
|
schema:
|
|
type: string
|
|
enum:
|
|
- exact
|
|
- prefix
|
|
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
|
|
description: |
|
|
Grants a current paid entitlement when the current effective state is
|
|
`free`.
|
|
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
|
|
description: |
|
|
Extends the current finite paid entitlement.
|
|
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
|
|
description: |
|
|
Revokes the current 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
|
|
description: |
|
|
Applies one new active 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
|
|
description: |
|
|
Removes the current active sanction for one `sanction_code`.
|
|
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
|
|
description: |
|
|
Creates one new active limit or replaces the current active record of
|
|
the same `limit_code`.
|
|
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
|
|
description: |
|
|
Removes the current active user-specific limit for one `limit_code`.
|
|
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"
|
|
/api/v1/internal/users/{user_id}/delete:
|
|
post:
|
|
tags:
|
|
- AdminUsers
|
|
operationId: deleteUser
|
|
summary: Soft-delete one regular-user account
|
|
description: |
|
|
Soft-deletes the account identified by `user_id`. The account record is
|
|
preserved for audit with a `deleted_at` timestamp. Subsequent external
|
|
auth, self-service, admin-read, and lobby-eligibility operations
|
|
addressing the same `user_id` return `404 subject_not_found`.
|
|
|
|
The command is idempotent per `user_id`: calling it after the account
|
|
is already soft-deleted returns `404 subject_not_found` and does not
|
|
re-emit the `user.lifecycle.deleted` event.
|
|
parameters:
|
|
- $ref: "#/components/parameters/UserIDPath"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/DeleteUserRequest"
|
|
responses:
|
|
"200":
|
|
description: Soft-delete applied successfully.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/DeleteUserResponse"
|
|
"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 and bound to the normalized filter set that produced it. Malformed or filter-mismatched tokens return `400 invalid_request`.
|
|
schema:
|
|
type: string
|
|
schemas:
|
|
UserID:
|
|
type: string
|
|
description: Stable regular-user identifier.
|
|
minLength: 1
|
|
Email:
|
|
type: string
|
|
format: email
|
|
description: |
|
|
Login and contact e-mail address. The service trims surrounding
|
|
whitespace and validates the value structurally, then treats the
|
|
trimmed value as the exact stored and lookup value. The service does
|
|
not lowercase or otherwise canonicalize e-mail before storage or exact
|
|
lookup.
|
|
UserName:
|
|
type: string
|
|
description: |
|
|
Immutable auto-generated platform handle in `player-<suffix>` form.
|
|
The suffix is eight characters drawn from a confusable-free
|
|
alphanumeric alphabet. Assigned once at account creation and never
|
|
changes thereafter.
|
|
pattern: "^player-[a-z0-9]{8}$"
|
|
minLength: 15
|
|
maxLength: 64
|
|
DisplayName:
|
|
type: string
|
|
description: |
|
|
Optional free-text user-facing label. Validated by
|
|
`pkg/util/string.go:ValidateTypeName`. Empty values are accepted and
|
|
represent no display name. Uniqueness is not enforced.
|
|
minLength: 0
|
|
maxLength: 30
|
|
LanguageTag:
|
|
type: string
|
|
description: |
|
|
BCP 47 language tag. User Service validates semantic correctness on
|
|
auth-driven creation and stores the canonical tag form.
|
|
minLength: 1
|
|
maxLength: 32
|
|
TimeZoneName:
|
|
type: string
|
|
description: |
|
|
IANA time zone name. User Service validates semantic correctness on
|
|
auth-driven creation and stores the trimmed caller value without
|
|
additional alias canonicalization.
|
|
minLength: 1
|
|
maxLength: 128
|
|
CountryCode:
|
|
type: string
|
|
description: |
|
|
ISO 3166-1 alpha-2 country code in uppercase ASCII form. The geo sync
|
|
command additionally rejects well-formed but unknown region codes.
|
|
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
|
|
- permanent_block
|
|
LimitCode:
|
|
type: string
|
|
description: |
|
|
Current supported user-specific limit codes. Retired legacy codes may
|
|
still exist in stored history for backward compatibility, but they are
|
|
not part of this write or read contract.
|
|
enum:
|
|
- max_owned_private_games
|
|
- max_pending_public_applications
|
|
- max_active_game_memberships
|
|
- max_registered_race_names
|
|
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
|
|
description: |
|
|
Frozen create-only initialization context used by the current
|
|
auth-facing ensure-by-email contract. `preferred_language` is
|
|
semantically validated as BCP 47 and stored in canonical tag form on
|
|
create. `time_zone` is semantically validated as an IANA time zone
|
|
name and stored after trim without additional alias canonicalization.
|
|
additionalProperties: false
|
|
required:
|
|
- preferred_language
|
|
- time_zone
|
|
properties:
|
|
preferred_language:
|
|
$ref: "#/components/schemas/LanguageTag"
|
|
description: |
|
|
Create-only initial preferred language. During the current rollout
|
|
`Auth / Session Service` sends a temporary `"en"` default and
|
|
forwards `time_zone`. Gateway-side geoip derivation is not part of
|
|
the current source-of-truth contract. Future derived values must
|
|
remain valid BCP 47 tags.
|
|
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
|
|
- registration_context
|
|
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"
|
|
description: |
|
|
Present for `existing` and `created`. A `created` outcome returns
|
|
the durable newly materialized `user_id` created together with an
|
|
initial auto-generated `user_name` handle and the free
|
|
entitlement snapshot. `display_name` defaults to empty for new
|
|
accounts.
|
|
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
|
|
description: |
|
|
Materialized current effective entitlement snapshot.
|
|
|
|
The current snapshot is read-optimized and repaired lazily when a
|
|
finite paid state has already reached `ends_at`, so callers do not
|
|
observe stale paid/free state.
|
|
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
|
|
description: |
|
|
Current supported active user-specific limit override. Retired legacy
|
|
limit codes are ignored on reads and are not returned.
|
|
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
|
|
EffectiveLimit:
|
|
type: object
|
|
additionalProperties: false
|
|
description: |
|
|
Materialized numeric quota after the frozen `free` or `paid` default
|
|
catalog is combined with any active user-specific override for the same
|
|
`limit_code`.
|
|
|
|
`max_owned_private_games` is meaningful only while the current
|
|
entitlement is paid and is omitted from free effective limits.
|
|
|
|
`max_active_game_memberships` applies only to public games.
|
|
|
|
`max_pending_public_applications` stores the total public-games budget.
|
|
`Game Lobby` subtracts current active public memberships from this
|
|
value and clamps at `0` to derive remaining pending-application
|
|
headroom.
|
|
required:
|
|
- limit_code
|
|
- value
|
|
properties:
|
|
limit_code:
|
|
$ref: "#/components/schemas/LimitCode"
|
|
value:
|
|
type: integer
|
|
minimum: 0
|
|
AccountView:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- user_id
|
|
- email
|
|
- user_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"
|
|
user_name:
|
|
$ref: "#/components/schemas/UserName"
|
|
display_name:
|
|
$ref: "#/components/schemas/DisplayName"
|
|
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
|
|
deleted_at:
|
|
type: string
|
|
format: date-time
|
|
description: |
|
|
Soft-delete timestamp. Present only when the account has been
|
|
soft-deleted by a trusted `DeleteUser` command. External reads by
|
|
stable `user_id` return `404 subject_not_found` for such accounts;
|
|
admin listings exclude them unless explicitly asked via the
|
|
`deleted` filter.
|
|
GetMyAccountResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- account
|
|
properties:
|
|
account:
|
|
$ref: "#/components/schemas/AccountView"
|
|
UpdateMyProfileRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
description: |
|
|
Accepts only `display_name`. An empty value is accepted and resets
|
|
any stored display name. Any other field (including the legacy
|
|
`race_name` payload) is rejected as `400 invalid_request` through
|
|
strict unknown-field handling.
|
|
required:
|
|
- display_name
|
|
properties:
|
|
display_name:
|
|
$ref: "#/components/schemas/DisplayName"
|
|
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
|
|
- active_sanctions
|
|
- effective_limits
|
|
- markers
|
|
properties:
|
|
exists:
|
|
type: boolean
|
|
user_id:
|
|
$ref: "#/components/schemas/UserID"
|
|
entitlement:
|
|
description: |
|
|
Current effective entitlement snapshot. Omitted when `exists=false`.
|
|
$ref: "#/components/schemas/EntitlementSnapshot"
|
|
active_sanctions:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/ActiveSanction"
|
|
effective_limits:
|
|
description: |
|
|
Materialized effective quotas for the current supported lobby
|
|
catalog. Unknown users return an empty array. Free users omit
|
|
`max_owned_private_games`.
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/EffectiveLimit"
|
|
markers:
|
|
$ref: "#/components/schemas/EligibilityMarkers"
|
|
SyncDeclaredCountryRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
description: |
|
|
Synchronizes the latest effective declared country selected by
|
|
`Geo Profile Service`. Repeating the current stored value is accepted
|
|
as a no-op.
|
|
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
|
|
description: |
|
|
Effective account mutation timestamp. Same-value no-op syncs return
|
|
the existing stored timestamp unchanged.
|
|
UserAdminView:
|
|
allOf:
|
|
- $ref: "#/components/schemas/AccountView"
|
|
UserLookupByEmailRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- email
|
|
properties:
|
|
email:
|
|
$ref: "#/components/schemas/Email"
|
|
UserLookupByUserNameRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- user_name
|
|
properties:
|
|
user_name:
|
|
$ref: "#/components/schemas/UserName"
|
|
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
|
|
description: |
|
|
Grants one current paid entitlement.
|
|
|
|
`plan_code=free` is invalid here. `starts_at` may be current or past,
|
|
but not future. Finite paid plans require `ends_at`, while
|
|
`paid_lifetime` forbids it.
|
|
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
|
|
description: Required for `paid_monthly` and `paid_yearly`; omitted for `paid_lifetime`.
|
|
ExtendEntitlementRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
description: |
|
|
Extends the current finite paid entitlement by appending one new paid
|
|
history segment.
|
|
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
|
|
description: |
|
|
Revokes the current effective paid entitlement and materializes a new
|
|
`free` snapshot immediately.
|
|
required:
|
|
- source
|
|
- reason_code
|
|
- actor
|
|
properties:
|
|
source:
|
|
type: string
|
|
reason_code:
|
|
type: string
|
|
actor:
|
|
$ref: "#/components/schemas/ActorRef"
|
|
EntitlementCommandResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
description: Resulting current effective entitlement snapshot after one
|
|
successful trusted entitlement command.
|
|
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"
|
|
DeleteUserRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
description: |
|
|
Soft-delete command payload. The caller is expected to be
|
|
`Admin Service`. `actor.type` must be non-empty; `actor.id` is
|
|
optional.
|
|
required:
|
|
- reason_code
|
|
- actor
|
|
properties:
|
|
reason_code:
|
|
type: string
|
|
actor:
|
|
$ref: "#/components/schemas/ActorRef"
|
|
DeleteUserResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- user_id
|
|
- deleted_at
|
|
properties:
|
|
user_id:
|
|
$ref: "#/components/schemas/UserID"
|
|
deleted_at:
|
|
type: string
|
|
format: date-time
|
|
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
|