{"openapi":"3.1.0","info":{"title":"Hub User API","contact":{"name":"Kipu Quantum GmbH","url":"https://kipu-quantum.com","email":"support-hub@kipu-quantum.com"},"version":"1.0"},"servers":[{"url":"https://api.hub.kipu-quantum.com/user-service","description":"Generated server url"}],"tags":[{"name":"User Settings","description":"Manage the profile of the currently authenticated user."},{"name":"Users","description":"Public read access to user profiles and profile images."},{"name":"Search","description":"Full-text search across user profiles."},{"name":"User Settings - Personal Access Tokens","description":"Manage personal access tokens of the currently authenticated user."},{"name":"User Registration Status","description":"Query the registration state of a user identified by email address."},{"name":"Authentication","description":"Endpoints for resolving personal access tokens into authenticated principals."}],"paths":{"/me":{"get":{"tags":["User Settings"],"summary":"Get the currently authenticated user","description":"Returns the full profile of the currently authenticated user, including email, personal details, and profile metadata (current position, homepage, about). If the user does not yet exist in the user-service database, it is provisioned just-in-time from the identity provider and then returned.","operationId":"getCurrentUser","responses":{"200":{"description":"The full profile of the authenticated user.","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDto"}}}},"401":{"description":"The request is not authenticated."}},"security":[{"oauth2":[]},{"apiKey":[]}]},"put":{"tags":["User Settings"],"summary":"Update the currently authenticated user","description":"Updates the editable profile fields (current position, homepage, about) of the currently authenticated user. Core identity fields such as email, first name, and last name are owned by the identity provider and cannot be modified through this endpoint. Returns the user profile with the updated values applied.","operationId":"updateCurrentUser","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserRequest"}}},"required":true},"responses":{"200":{"description":"The updated user profile.","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDto"}}}},"400":{"description":"The request body failed validation."},"401":{"description":"The request is not authenticated."}},"security":[{"oauth2":[]},{"apiKey":[]}]},"delete":{"tags":["User Settings"],"summary":"Delete the currently authenticated user","description":"Permanently deletes the currently authenticated user together with all associated resources (personal access tokens, profile image, profile metadata). The user is also removed from the identity provider. The operation cannot be undone. Responds with `204 No Content` once the deletion has completed.","operationId":"deleteCurrentUser","responses":{"204":{"description":"The user has been deleted successfully."},"401":{"description":"The request is not authenticated."}},"security":[{"oauth2":[]},{"apiKey":[]}]}},"/me/access-tokens/default":{"put":{"tags":["User Settings - Personal Access Tokens"],"summary":"Regenerate the default personal access token","description":"Rotates the default personal access token of the currently authenticated user and returns the newly generated raw token value. The previous default token is invalidated immediately — all clients relying on it must be updated. The new value is only returned by this endpoint; it cannot be retrieved later.","operationId":"regenerateDefaultPersonalAccessToken","responses":{"200":{"description":"The default access token was regenerated. The new raw value is returned.","content":{"*/*":{"schema":{"$ref":"#/components/schemas/DefaultAccessTokenDto"}}}},"401":{"description":"The request is not authenticated."}},"security":[{"oauth2":[]},{"apiKey":[]}]}},"/me/profile-image":{"post":{"tags":["User Settings"],"summary":"Upload the profile image of the currently authenticated user","description":"Uploads and replaces the profile image of the currently authenticated user. The image is supplied as a multipart form part named `file`. Common image formats (e.g. JPEG, PNG) are accepted. Uploading a new image overwrites the previous one. Responds with `204 No Content` once the image has been persisted.","operationId":"updateCurrentUserProfileImage","requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"The image file to upload. Common image formats (JPEG, PNG) are accepted."}},"required":["file"]}}}},"responses":{"204":{"description":"The profile image has been stored successfully."},"400":{"description":"The supplied file is missing, empty, or not an accepted image format."},"401":{"description":"The request is not authenticated."}},"security":[{"oauth2":[]},{"apiKey":[]}]},"delete":{"tags":["User Settings"],"summary":"Delete the profile image of the currently authenticated user","description":"Removes the profile image of the currently authenticated user. The operation is idempotent: if no profile image is currently set, the request still succeeds. Responds with `204 No Content` once the image has been removed.","operationId":"deleteCurrentUserProfileImage","responses":{"204":{"description":"The profile image has been removed successfully."},"401":{"description":"The request is not authenticated."}},"security":[{"oauth2":[]},{"apiKey":[]}]}},"/me/access-tokens":{"get":{"tags":["User Settings - Personal Access Tokens"],"summary":"List personal access tokens","description":"Returns all personal access tokens of the currently authenticated user together with the current default access token value. The raw token `value` is only returned immediately after creation and is not included on subsequent reads — long-lived tokens are identifiable only by their id, name, and metadata.","operationId":"getPersonalAccessTokens","responses":{"200":{"description":"The personal access tokens of the authenticated user.","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AccessTokensDto"}}}},"401":{"description":"The request is not authenticated."}},"security":[{"oauth2":[]},{"apiKey":[]}]},"post":{"tags":["User Settings - Personal Access Tokens"],"summary":"Create a personal access token","description":"Creates a new personal access token for the currently authenticated user. The raw token `value` is returned exactly once in the response and cannot be retrieved later — clients must persist it immediately after creation. If `expiresAt` is omitted the token does not expire; otherwise the token becomes invalid at the end of the specified day.","operationId":"createPersonalAccessToken","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAccessTokenRequest"}}},"required":true},"responses":{"200":{"description":"The newly created personal access token, including the raw `value`.","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AccessTokenDto"}}}},"400":{"description":"The request body failed validation."},"401":{"description":"The request is not authenticated."}},"security":[{"oauth2":[]},{"apiKey":[]}]}},"/authorize":{"post":{"tags":["Authentication"],"summary":"Authorize a personal access token","description":"Validates the personal access token supplied in the `X-Auth-Token` header and returns the authenticated principal (user id and token metadata). Primarily consumed by internal services (e.g. the API gateway) to resolve a raw access token into the authenticated user context before forwarding the request downstream. Responds with `401 Unauthorized` if the token is missing, expired, malformed, or unknown.","operationId":"authorizeByPersonalAccessToken","parameters":[{"name":"X-Auth-Token","in":"header","description":"Raw personal access token to authorize. Required; sent as the `X-Auth-Token` header value.","required":false,"schema":{"type":"string"},"example":"kqh_plqk_RhtrP2EErcN6DwiNWvP0Ha4U1dcNdWdrViQJhEQ9c5"}],"responses":{"200":{"description":"Token is valid. The authenticated principal is returned.","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PersonalAccessTokenPrincipal"}}}},"401":{"description":"Token is missing, expired, or not a known personal access token."},"403":{"description":"Token is valid but the associated user is not permitted to authenticate."}}}},"/users/{id}":{"get":{"tags":["Users"],"summary":"Get a user by id","description":"Returns a public overview (id, first name, last name) of the user identified by `id`. This endpoint exposes only non-sensitive profile fields and is intended for rendering user references throughout the platform. For the full profile of the authenticated user, use `GET /me`.","operationId":"getUserById","parameters":[{"name":"id","in":"path","description":"Unique identifier of the user. Typically the Keycloak subject (sub) of the authenticated user.","required":true,"schema":{"type":"string"},"example":"d8c5f0e2-6d36-4c20-9c9a-8c3d5f0e2b7a"}],"responses":{"200":{"description":"The user overview is returned.","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserOverviewDto"}}}},"401":{"description":"The request is not authenticated."},"403":{"description":"The authenticated caller is not permitted to access this user."},"404":{"description":"No user exists with the given id."}}},"head":{"tags":["Users"],"summary":"Check whether a user exists","description":"Lightweight existence check for a user. Returns `204 No Content` if a user with the given id exists and `404 Not Found` otherwise. No response body is ever emitted. Intended for callers that want to validate a user reference without transferring the full user payload.","operationId":"checkUserExists","parameters":[{"name":"id","in":"path","description":"Unique identifier of the user. Typically the Keycloak subject (sub) of the authenticated user.","required":true,"schema":{"type":"string"},"example":"d8c5f0e2-6d36-4c20-9c9a-8c3d5f0e2b7a"}],"responses":{"204":{"description":"A user with the given id exists."},"404":{"description":"No user exists with the given id."}}}},"/users/{id}/profile-image":{"get":{"tags":["Users"],"summary":"Get a user's profile image","description":"Returns the binary profile image of the user identified by `id`. The response is served with a `Cache-Control` header allowing client-side caching for up to ten days (with mandatory revalidation) to avoid repeated fetches. Responds with `204 No Content` if the user exists but has not uploaded a profile image, and `404 Not Found` if the user does not exist.","operationId":"getUserProfileImage","parameters":[{"name":"id","in":"path","description":"Unique identifier of the user. Typically the Keycloak subject (sub) of the authenticated user.","required":true,"schema":{"type":"string"},"example":"d8c5f0e2-6d36-4c20-9c9a-8c3d5f0e2b7a"}],"responses":{"200":{"description":"Profile image returned. The `Content-Type` header reflects the stored image format.","content":{"*/*":{"schema":{"type":"string","format":"byte"}}}},"204":{"description":"The user exists but has no profile image set."},"404":{"description":"No user exists with the given id."}}}},"/user-registration-status":{"get":{"tags":["User Registration Status"],"summary":"Get the registration status of a user","description":"Returns the registration status of a user identified by their email address. Used by the registration flow to determine whether a user exists and, if so, which step the user still has to complete (e.g. verifying their email address or waiting for administrative approval). Possible status values are `PENDING`, `APPROVED`, `REJECTED`, `NOT_FOUND`, and `EMAIL_NOT_VERIFIED`.","operationId":"getUserRegistrationStatus","parameters":[{"name":"email","in":"query","description":"Email address of the user whose registration status should be looked up.","required":true,"schema":{"type":"string"},"example":"john.doe@example.com"}],"responses":{"200":{"description":"The current registration status of the user.","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RegistrationResponse"}}}},"400":{"description":"The `email` query parameter is missing or malformed."},"401":{"description":"The request is not authenticated."}}}},"/search":{"get":{"tags":["Search"],"summary":"Search for users","description":"Searches for users whose profile matches the supplied query string. Matching is performed case-insensitively against first name, last name, username, and email using a substring match. The result list is capped at a sensible default size; the endpoint does not expose pagination parameters. Returns an empty list if nothing matches — it does not respond with `404`.","operationId":"searchUsers","parameters":[{"name":"q","in":"query","description":"Free-text query matched against first name, last name, username, and email (case-insensitive, substring match).","required":true,"schema":{"type":"string","minLength":1},"example":"john"}],"responses":{"200":{"description":"The list of matching users, possibly empty.","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserSearchDto"}}}}},"400":{"description":"The `q` query parameter is missing or blank."},"401":{"description":"The request is not authenticated."}},"security":[{"oauth2":[]},{"apiKey":[]}]}},"/me/access-tokens/{id}":{"delete":{"tags":["User Settings - Personal Access Tokens"],"summary":"Delete a personal access token","description":"Permanently deletes the personal access token identified by `id`. The token must belong to the currently authenticated user; attempts to delete another user's token are rejected with `404 Not Found`. Any further request authenticated with the deleted token will fail with `401 Unauthorized`. The operation cannot be undone.","operationId":"deletePersonalAccessToken","parameters":[{"name":"id","in":"path","description":"Unique identifier of the personal access token to delete.","required":true,"schema":{"type":"string","format":"uuid"},"example":"a6f3e8a0-1c4b-4d2f-8d5a-1f2e3a4b5c6d"}],"responses":{"204":{"description":"The access token has been deleted."},"401":{"description":"The request is not authenticated."},"403":{"description":"The authenticated caller is not permitted to delete this token."},"404":{"description":"No access token with the given id exists for the authenticated user."}},"security":[{"oauth2":[]},{"apiKey":[]}]}}},"components":{"schemas":{"UpdateUserRequest":{"type":"object","description":"Payload to update the editable profile fields of the currently authenticated user. All fields are optional; `null` or absent fields leave the current value unchanged.","properties":{"currentPosition":{"type":["string","null"],"description":"Current professional position or role of the user. Free-form text intended for display on the user's profile.","example":"Senior Research Engineer"},"homepage":{"type":["string","null"],"description":"Personal or professional homepage of the user. Expected to be a fully qualified URL.","example":"https://example.com/john-doe"},"about":{"type":["string","null"],"description":"Short biography or description shown on the user's profile.","example":"Quantum software engineer with a background in distributed systems."}}},"UserDto":{"type":"object","description":"Full profile of the currently authenticated user, including identity information managed by the identity provider and editable profile metadata.","properties":{"id":{"type":"string","description":"Unique identifier of the user (matches the Keycloak `sub` claim).","example":"d8c5f0e2-6d36-4c20-9c9a-8c3d5f0e2b7a"},"username":{"type":"string","description":"Login name of the user. Typically equal to the email address.","example":"john.doe@example.com"},"email":{"type":"string","description":"Email address of the user. Managed by the identity provider.","example":"john.doe@example.com"},"firstname":{"type":"string","description":"First (given) name of the user.","example":"John"},"lastname":{"type":"string","description":"Last (family) name of the user.","example":"Doe"},"currentPosition":{"type":["string","null"],"description":"Current professional position or role of the user.","example":"Senior Research Engineer"},"homepage":{"type":["string","null"],"description":"Personal or professional homepage of the user.","example":"https://example.com/john-doe"},"about":{"type":["string","null"],"description":"Short biography or description shown on the user's profile.","example":"Quantum software engineer with a background in distributed systems."}}},"DefaultAccessTokenDto":{"type":"object","description":"Response wrapping the newly generated default personal access token value.","properties":{"defaultToken":{"type":"string","description":"Raw value of the regenerated default personal access token. Returned only once by the rotation endpoint and not retrievable later.","example":"kqh_plqk_RhtrP2EErcN6DwiNWvP0Ha4U1dcNdWdrViQJhEQ9c5"}}},"CreateAccessTokenRequest":{"type":"object","description":"Payload for creating a new personal access token.","properties":{"name":{"type":"string","description":"Human-readable name for the token, shown in the user interface to help identify the token's purpose.","example":"My Access Token","minLength":1},"expiresAt":{"type":["string","null"],"format":"date","description":"Expiration date of the token in ISO-8601 date format (yyyy-MM-dd). The token becomes invalid at the end of the specified day. If omitted, the token does not expire.","example":"2031-01-01","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},"required":["name"]},"AccessTokenDto":{"type":"object","description":"Represents a personal access token belonging to a user.","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the personal access token.","example":"a6f3e8a0-1c4b-4d2f-8d5a-1f2e3a4b5c6d"},"name":{"type":"string","description":"Human-readable name of the token, chosen by the user at creation time.","example":"My Access Token","minLength":1},"createdAt":{"type":"string","description":"Timestamp at which the token was created (formatted as `yyyy-MM-dd HH:mm:ss`, UTC).","example":"2021-01-01 00:00:00","pattern":"^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}$"},"usedAt":{"type":["string","null"],"description":"Timestamp at which the token was last used to authenticate a request (formatted as `yyyy-MM-dd HH:mm:ss`, UTC). `null` if the token has never been used.","example":"2021-01-01 12:00:00","pattern":"^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}$"},"expiresAt":{"type":["string","null"],"format":"date","description":"Expiration date of the token in ISO-8601 date format (yyyy-MM-dd). `null` indicates that the token does not expire.","example":"2031-01-01","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"value":{"type":["string","null"],"description":"Raw token value. Returned only once — immediately after the token was created — and `null` on subsequent reads. Clients must persist the value when it is first returned, as it cannot be retrieved later.","example":"kqh_plqk_RhtrP2EErcN6DwiNWvP0Ha4U1dcNdWdrViQJhEQ9c5"}},"required":["name"]},"PersonalAccessTokenPrincipal":{"type":"object","description":"Authenticated principal resolved from a personal access token. Returned by the authorize endpoint so that downstream services can identify the user and the access token that was used to authenticate the request.","properties":{"id":{"type":"string","description":"Unique identifier of the authenticated user (matches the Keycloak `sub` claim).","example":"d8c5f0e2-6d36-4c20-9c9a-8c3d5f0e2b7a"},"accessToken":{"$ref":"#/components/schemas/AccessTokenDto","description":"Metadata of the personal access token used to authenticate the request. The raw token `value` is never included in this response."}}},"UserOverviewDto":{"type":"object","description":"Minimal public representation of a user, exposing only non-sensitive fields suitable for rendering user references across the platform.","properties":{"id":{"type":"string","description":"Unique identifier of the user (matches the Keycloak `sub` claim).","example":"d8c5f0e2-6d36-4c20-9c9a-8c3d5f0e2b7a"},"firstname":{"type":"string","description":"First (given) name of the user.","example":"John"},"lastname":{"type":"string","description":"Last (family) name of the user.","example":"Doe"}}},"RegistrationResponse":{"type":"object","description":"Registration status of a user, used by the registration flow to decide the next step the user has to complete.","properties":{"status":{"type":"string","description":"Current registration status of the user. `APPROVED` — registration is complete; `PENDING` — awaiting administrative approval; `REJECTED` — registration was rejected; `NOT_FOUND` — no user with the given email address exists; `EMAIL_NOT_VERIFIED` — the user still needs to verify their email address.","enum":["PENDING","APPROVED","REJECTED","NOT_FOUND","EMAIL_NOT_VERIFIED"],"example":"APPROVED"},"message":{"type":["string","null"],"description":"Human-readable message that accompanies the status, suitable for showing to end users.","example":"Your account has been approved. You can now sign in."}}},"UserSearchDto":{"type":"object","description":"Entry in the user search result list. Contains just enough information to identify and render the user to the caller.","properties":{"id":{"type":"string","description":"Unique identifier of the matched user (matches the Keycloak `sub` claim).","example":"d8c5f0e2-6d36-4c20-9c9a-8c3d5f0e2b7a"},"firstname":{"type":"string","description":"First (given) name of the matched user.","example":"John"},"lastname":{"type":"string","description":"Last (family) name of the matched user.","example":"Doe"}}},"AccessTokensDto":{"type":"object","description":"Container for the personal access tokens of the currently authenticated user, together with the raw value of the default access token.","properties":{"defaultToken":{"type":"string","description":"Raw value of the user's current default personal access token. Used to authenticate requests that do not specify a particular token.","example":"kqh_plqk_RhtrP2EErcN6DwiNWvP0Ha4U1dcNdWdrViQJhEQ9c5"},"accessTokens":{"type":"array","description":"All personal access tokens issued for the authenticated user. May be empty.","items":{"$ref":"#/components/schemas/AccessTokenDto"}}}}},"securitySchemes":{"apiKey":{"type":"apiKey","name":"X-Auth-Token","in":"header"},"oauth2":{"type":"oauth2","flows":{"password":{"tokenUrl":"https://login.hub.kipu-quantum.com/realms/planqk/protocol/openid-connect/token","refreshUrl":"https://login.hub.kipu-quantum.com/realms/planqk/protocol/openid-connect/token","scopes":{"profile":""}}}}}}}