openapi: 3.0.3 info: title: Entrosity Hub API version: 0.1.0 description: > Identity and access for all Entrosity products: sign-in, sessions, two-factor authentication, organizations, members, product access and platform administration. This file is the source of truth for the platform server interfaces (oapi-codegen), request validation, and the platform web app's types (openapi-typescript). Run `make gen` after editing. Authentication: `Authorization: Bearer ` (15 min JWT, audience `platform`). The refresh token lives in the httpOnly cookie `platform_rt` scoped to `/api/platform/v1/auth` and is rotated on every `/auth/refresh`. Cookie-authenticated routes (`/auth/refresh`, `/auth/logout`, `/auth/product-token`) only accept same-origin requests with a JSON body. Products (e.g. Entrosity Axis at `/axis`) do not accept platform access tokens: the browser exchanges the session cookie for a short-lived product token (`POST /auth/product-token`), signed with Ed25519 and verifiable with the keys at `/.well-known/jwks.json`. Product tokens carry no roles; products read roles from their own copy of the access data, which they pull from the platform's internal API (not served here). Every route's access rule (public, authenticated, organization admin, platform admin) is declared in `platform/backend/internal/http/access.go` and enforced before the handler runs. Requests for another organization's resources return 404. servers: - url: /api/platform/v1 tags: - name: system - name: auth - name: me - name: organizations - name: admin security: - bearerAuth: [] paths: /healthz: get: operationId: getHealthz summary: Liveness probe tags: - system security: [] responses: '200': description: The API process is up. content: application/json: schema: $ref: '#/components/schemas/Health' default: $ref: '#/components/responses/Problem' /.well-known/jwks.json: get: operationId: getJwks summary: Public keys that verify product and step-up tokens (Ed25519) tags: - system security: [] responses: '200': description: JSON Web Key Set. content: application/json: schema: $ref: '#/components/schemas/Jwks' default: $ref: '#/components/responses/Problem' /auth/login: post: operationId: login summary: Sign in with e-mail and password (and TOTP code when enabled) tags: - auth security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LoginRequest' responses: '200': $ref: '#/components/responses/SessionWithCookie' default: $ref: '#/components/responses/Problem' /auth/refresh: post: operationId: refreshSession summary: Exchange the session cookie for a new access token (rotates the cookie) tags: - auth security: [] parameters: - $ref: '#/components/parameters/SessionCookie' responses: '200': $ref: '#/components/responses/SessionWithCookie' default: $ref: '#/components/responses/Problem' /auth/logout: post: operationId: logout summary: End the session and clear the session cookie tags: - auth security: [] parameters: - $ref: '#/components/parameters/SessionCookie' responses: '204': description: Signed out. headers: Set-Cookie: $ref: '#/components/headers/SetCookie' default: $ref: '#/components/responses/Problem' /auth/product-token: post: operationId: createProductToken summary: Short-lived token for a product (e.g. RMM), from the session cookie description: | Returns a 5-minute Ed25519-signed JWT with audience = the product id. It never rotates or revokes the session cookie, so it can run while another tab refreshes. 401 without a valid session; 403 `no_product_access` when the user has no role in the product; 403 `totp_setup_required` when an organization of the user requires two-factor authentication and it is not enabled. tags: - auth security: [] parameters: - $ref: '#/components/parameters/SessionCookie' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ProductTokenRequest' responses: '200': description: Product token. content: application/json: schema: $ref: '#/components/schemas/ProductToken' default: $ref: '#/components/responses/Problem' /auth/step-up: post: operationId: createStepUpToken summary: >- Confirm the password for a sensitive product action (2-minute, single-use token) description: | For actions a product protects with the password (e.g. deleting an RMM enrollment token). Authenticated by the session cookie, like product tokens, so product web apps can ask for it. The token is bound to the session and can be used once. A wrong password is a 422 on `password`; 401 without a valid session. Rate-limited like sign-in. tags: - auth security: [] parameters: - $ref: '#/components/parameters/SessionCookie' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/StepUpRequest' responses: '200': description: Step-up token. content: application/json: schema: $ref: '#/components/schemas/ProductToken' default: $ref: '#/components/responses/Problem' /auth/password/forgot: post: operationId: forgotPassword summary: E-mail a password reset link (always 202) tags: - auth security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ForgotPasswordRequest' responses: '202': description: Accepted. A link is sent if the account exists. default: $ref: '#/components/responses/Problem' /auth/password/reset: post: operationId: resetPassword summary: Set a new password with a reset token (signs out all sessions) tags: - auth security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ResetPasswordRequest' responses: '204': description: Password changed. default: $ref: '#/components/responses/Problem' /auth/password/change: post: operationId: changePassword summary: Change the signed-in user's password (other sessions are signed out) description: | Requires the current password. The calling session stays signed in; all other sessions of the user are signed out. A wrong current password or a weak new one is a 422 naming the field (`current_password`, `new_password`). Rate-limited like sign-in. tags: - auth requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ChangePasswordRequest' responses: '204': description: Password changed. default: $ref: '#/components/responses/Problem' /auth/email/confirm: post: operationId: confirmEmailChange summary: Apply an e-mail change with the link's token (signs out all sessions) description: | The token comes from the link e-mailed to the new address by `POST /me/email`. The old address is told about the change. An unknown, used or expired token is a 400 `invalid_token`; an address taken by another account since the request is a 422 on `new_email`. tags: - auth security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ConfirmEmailChangeRequest' responses: '204': description: E-mail changed. default: $ref: '#/components/responses/Problem' /auth/totp/setup: post: operationId: setupTotp summary: Start enrolling an authenticator app tags: - auth responses: '200': description: Secret and QR code (shown once). content: application/json: schema: $ref: '#/components/schemas/TotpSetup' default: $ref: '#/components/responses/Problem' /auth/totp/confirm: post: operationId: confirmTotp summary: >- Confirm the authenticator with a code; enables 2FA and returns recovery codes tags: - auth requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TotpConfirmRequest' responses: '200': description: 2FA enabled. content: application/json: schema: $ref: '#/components/schemas/RecoveryCodes' default: $ref: '#/components/responses/Problem' /auth/totp: delete: operationId: disableTotp summary: >- Disable 2FA (requires the password; refused while an organization requires it) tags: - auth requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PasswordConfirmRequest' responses: '204': description: 2FA disabled. default: $ref: '#/components/responses/Problem' /me: get: operationId: getMe summary: The signed-in user tags: - me responses: '200': description: Current user. content: application/json: schema: $ref: '#/components/schemas/User' default: $ref: '#/components/responses/Problem' patch: operationId: updateMe summary: Change my display name tags: - me requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateMeRequest' responses: '200': description: Updated user. content: application/json: schema: $ref: '#/components/schemas/User' default: $ref: '#/components/responses/Problem' /me/email: post: operationId: requestEmailChange summary: Ask to change my e-mail (a confirmation link goes to the new address) description: | Requires the current password. Nothing changes until the link is opened; only the newest link works. A wrong password or an invalid, unchanged or taken address is a 422 naming the field (`current_password`, `new_email`). Rate-limited like sign-in. tags: - me requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RequestEmailChangeRequest' responses: '202': description: Confirmation link sent to the new address. default: $ref: '#/components/responses/Problem' /me/sessions: get: operationId: listMySessions summary: My signed-in sessions (browsers) tags: - me responses: '200': description: Active sessions, newest first. content: application/json: schema: $ref: '#/components/schemas/SessionList' default: $ref: '#/components/responses/Problem' /me/sessions/{sessionID}: delete: operationId: revokeMySession summary: Sign out one of my sessions tags: - me parameters: - $ref: '#/components/parameters/SessionID' responses: '204': description: Session signed out. default: $ref: '#/components/responses/Problem' /me/products: get: operationId: listMyProducts summary: The products I can open, per organization (the launcher) tags: - me responses: '200': description: Launcher entries. content: application/json: schema: $ref: '#/components/schemas/LauncherEntryList' default: $ref: '#/components/responses/Problem' /me/organizations: get: operationId: listMyOrganizations summary: The organizations I belong to, with my roles tags: - me responses: '200': description: My memberships. content: application/json: schema: $ref: '#/components/schemas/MembershipList' default: $ref: '#/components/responses/Problem' /products: get: operationId: listProducts summary: Products and their roles (for role pickers) description: Products in beta are listed to platform admins only. tags: - me responses: '200': description: Products. content: application/json: schema: $ref: '#/components/schemas/ProductList' default: $ref: '#/components/responses/Problem' /invitations/{token}: get: operationId: getInvitation summary: Public details of an open invitation tags: - auth security: [] parameters: - $ref: '#/components/parameters/InvitationToken' responses: '200': description: Invitation. content: application/json: schema: $ref: '#/components/schemas/InvitationInfo' default: $ref: '#/components/responses/Problem' /invitations/{token}/accept: post: operationId: acceptInvitation summary: >- Accept an invitation (creates the account, or adds access to the signed-in account) description: | For a new e-mail address, `display_name` and `password` are required and the response signs the new user in. For an address that already has an account, the caller must be signed in as that account (bearer token); the invitation's access is added and the current session is returned. Signed in as another account: 409 `invitation_other_account`. tags: - auth security: - {} - bearerAuth: [] parameters: - $ref: '#/components/parameters/InvitationToken' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AcceptInvitationRequest' responses: '200': $ref: '#/components/responses/SessionWithCookie' default: $ref: '#/components/responses/Problem' /orgs/{orgID}: get: operationId: getOrganization summary: An organization I administer tags: - organizations parameters: - $ref: '#/components/parameters/OrgID' responses: '200': description: Organization. content: application/json: schema: $ref: '#/components/schemas/Organization' default: $ref: '#/components/responses/Problem' /orgs/{orgID}/audit: get: operationId: listOrganizationAudit summary: Audit log of one organization (its admins and platform admins) tags: - organizations parameters: - $ref: '#/components/parameters/OrgID' - $ref: '#/components/parameters/Page' - $ref: '#/components/parameters/PageSize' - name: actor_user_id in: query schema: type: string format: uuid - name: action in: query schema: type: string maxLength: 100 - name: from in: query schema: type: string format: date-time - name: to in: query schema: type: string format: date-time responses: '200': description: The organization's audit entries, newest first. content: application/json: schema: $ref: '#/components/schemas/AuditList' default: $ref: '#/components/responses/Problem' /orgs/{orgID}/members: get: operationId: listOrganizationMembers summary: Members and open invitations of an organization tags: - organizations parameters: - $ref: '#/components/parameters/OrgID' responses: '200': description: Members and invitations. content: application/json: schema: $ref: '#/components/schemas/MemberDirectory' default: $ref: '#/components/responses/Problem' /orgs/{orgID}/members/{userID}: patch: operationId: updateOrganizationMember summary: Change a member's organization role or product roles description: | `product_roles` replaces all product roles of the member in this organization; only products enabled for the organization are accepted. Demoting the last organization admin is a 409 `last_org_admin`. tags: - organizations parameters: - $ref: '#/components/parameters/OrgID' - $ref: '#/components/parameters/UserID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateMemberRequest' responses: '200': description: Updated member. content: application/json: schema: $ref: '#/components/schemas/Member' default: $ref: '#/components/responses/Problem' delete: operationId: removeOrganizationMember summary: Remove a member from the organization (the account stays) tags: - organizations parameters: - $ref: '#/components/parameters/OrgID' - $ref: '#/components/parameters/UserID' responses: '204': description: Removed. default: $ref: '#/components/responses/Problem' /orgs/{orgID}/invitations: post: operationId: inviteOrganizationMember summary: >- Invite someone to the organization (e-mail, organization role, product roles) tags: - organizations parameters: - $ref: '#/components/parameters/OrgID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/InviteMemberRequest' responses: '201': description: Invitation sent. content: application/json: schema: $ref: '#/components/schemas/Invitation' default: $ref: '#/components/responses/Problem' /orgs/{orgID}/invitations/{invitationID}: delete: operationId: revokeOrganizationInvitation summary: Revoke an open invitation tags: - organizations parameters: - $ref: '#/components/parameters/OrgID' - $ref: '#/components/parameters/InvitationID' responses: '204': description: Revoked. default: $ref: '#/components/responses/Problem' /admin/products/{productID}: patch: operationId: updateProduct summary: Put a product in beta (platform admins only) or release it tags: - admin parameters: - $ref: '#/components/parameters/ProductID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateProductRequest' responses: '200': description: Updated. content: application/json: schema: $ref: '#/components/schemas/Product' default: $ref: '#/components/responses/Problem' /admin/overview: get: operationId: getAdminOverview summary: Counters for the platform admin home page tags: - admin responses: '200': description: Overview. content: application/json: schema: $ref: '#/components/schemas/AdminOverview' default: $ref: '#/components/responses/Problem' /admin/organizations: get: operationId: listOrganizations summary: All organizations tags: - admin parameters: - $ref: '#/components/parameters/Page' - $ref: '#/components/parameters/PageSize' - $ref: '#/components/parameters/Query' - name: status in: query schema: $ref: '#/components/schemas/OrgStatus' responses: '200': description: Organizations. content: application/json: schema: $ref: '#/components/schemas/OrganizationList' default: $ref: '#/components/responses/Problem' post: operationId: createOrganization summary: >- Create an organization, enable products and optionally invite its first admin tags: - admin requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateOrganizationRequest' responses: '201': description: Created. content: application/json: schema: $ref: '#/components/schemas/Organization' default: $ref: '#/components/responses/Problem' /admin/organizations/{orgID}: get: operationId: getAdminOrganization summary: One organization tags: - admin parameters: - $ref: '#/components/parameters/OrgID' responses: '200': description: Organization. content: application/json: schema: $ref: '#/components/schemas/Organization' default: $ref: '#/components/responses/Problem' patch: operationId: updateOrganization summary: Rename, suspend or reactivate an organization, or change its settings tags: - admin parameters: - $ref: '#/components/parameters/OrgID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateOrganizationRequest' responses: '200': description: Updated. content: application/json: schema: $ref: '#/components/schemas/Organization' default: $ref: '#/components/responses/Problem' delete: operationId: suspendOrganization summary: >- Suspend an organization (members lose access to its products; no data is deleted) tags: - admin parameters: - $ref: '#/components/parameters/OrgID' responses: '204': description: Suspended. default: $ref: '#/components/responses/Problem' /admin/organizations/{orgID}/products/{productID}: put: operationId: enableOrganizationProduct summary: Give an organization access to a product tags: - admin parameters: - $ref: '#/components/parameters/OrgID' - $ref: '#/components/parameters/ProductID' responses: '200': description: Updated organization. content: application/json: schema: $ref: '#/components/schemas/Organization' default: $ref: '#/components/responses/Problem' delete: operationId: disableOrganizationProduct summary: >- Take a product away from an organization (its members' roles in it are removed) description: | The product keeps its data for the organization (RMM suspends the tenant); enabling the product again restores access once roles are given again. tags: - admin parameters: - $ref: '#/components/parameters/OrgID' - $ref: '#/components/parameters/ProductID' responses: '200': description: Updated organization. content: application/json: schema: $ref: '#/components/schemas/Organization' default: $ref: '#/components/responses/Problem' /admin/users: get: operationId: listUsers summary: All users tags: - admin parameters: - $ref: '#/components/parameters/Page' - $ref: '#/components/parameters/PageSize' - $ref: '#/components/parameters/Query' - name: status in: query schema: $ref: '#/components/schemas/UserStatus' - name: organization_id in: query schema: type: string format: uuid - name: platform_admin in: query schema: type: boolean responses: '200': description: Users. content: application/json: schema: $ref: '#/components/schemas/AdminUserList' default: $ref: '#/components/responses/Problem' /admin/users/{userID}: get: operationId: getUser summary: One user with all memberships and product roles tags: - admin parameters: - $ref: '#/components/parameters/UserID' responses: '200': description: User. content: application/json: schema: $ref: '#/components/schemas/AdminUser' default: $ref: '#/components/responses/Problem' patch: operationId: updateUser summary: Change a user's name, status or platform admin flag description: | Disabling signs the user out everywhere. Guard rails (409): `self_change` (you cannot disable yourself or drop your own platform admin flag), `last_platform_admin`. tags: - admin parameters: - $ref: '#/components/parameters/UserID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateUserRequest' responses: '200': description: Updated user. content: application/json: schema: $ref: '#/components/schemas/AdminUser' default: $ref: '#/components/responses/Problem' delete: operationId: deleteUser summary: >- Delete a user (signs out, removes all access; the record is kept for attribution) tags: - admin parameters: - $ref: '#/components/parameters/UserID' responses: '204': description: Deleted. default: $ref: '#/components/responses/Problem' /admin/users/{userID}/access: put: operationId: setUserAccess summary: Replace a user's organization memberships and product roles description: | The whole list is replaced: organizations missing from it are removed. Product roles must be roles of products enabled for that organization. Removing the last admin of an organization is a 409 `last_org_admin`. tags: - admin parameters: - $ref: '#/components/parameters/UserID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SetUserAccessRequest' responses: '200': description: Updated user. content: application/json: schema: $ref: '#/components/schemas/AdminUser' default: $ref: '#/components/responses/Problem' /admin/users/{userID}/revoke-sessions: post: operationId: revokeUserSessions summary: Sign a user out everywhere tags: - admin parameters: - $ref: '#/components/parameters/UserID' responses: '204': description: Signed out. default: $ref: '#/components/responses/Problem' /admin/users/{userID}/reset-auth: post: operationId: resetUserAuth summary: Reset a user's two-factor authentication, password, or both description: | `two_factor` turns two-factor authentication off (the authenticator and recovery codes stop working; the user can set it up again). `password` replaces the password with one nobody knows and e-mails the user a link, valid for 24 hours, to choose a new one. Either way the user is signed out everywhere. Errors: 422 when neither is chosen; 409 `self_reset` (use your own account page), `user_not_active` (a password reset needs an active user), `nothing_to_reset` (two-factor authentication is not set up). tags: - admin parameters: - $ref: '#/components/parameters/UserID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ResetUserAuthRequest' responses: '200': description: Updated user. content: application/json: schema: $ref: '#/components/schemas/AdminUser' default: $ref: '#/components/responses/Problem' /admin/invitations: get: operationId: listInvitations summary: All open invitations tags: - admin responses: '200': description: Open invitations, newest first. content: application/json: schema: $ref: '#/components/schemas/InvitationList' default: $ref: '#/components/responses/Problem' post: operationId: createInvitation summary: >- Invite someone (platform admin, and/or to an organization with product roles) tags: - admin requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateInvitationRequest' responses: '201': description: Invitation sent. content: application/json: schema: $ref: '#/components/schemas/Invitation' default: $ref: '#/components/responses/Problem' /admin/invitations/{invitationID}: delete: operationId: revokeInvitation summary: Revoke an open invitation tags: - admin parameters: - $ref: '#/components/parameters/InvitationID' responses: '204': description: Revoked. default: $ref: '#/components/responses/Problem' /admin/audit: get: operationId: listAudit summary: Platform audit log tags: - admin parameters: - $ref: '#/components/parameters/Page' - $ref: '#/components/parameters/PageSize' - name: organization_id in: query schema: type: string format: uuid - name: actor_user_id in: query schema: type: string format: uuid - name: action in: query schema: type: string maxLength: 100 - name: from in: query schema: type: string format: date-time - name: to in: query schema: type: string format: date-time responses: '200': description: Audit entries, newest first. content: application/json: schema: $ref: '#/components/schemas/AuditList' default: $ref: '#/components/responses/Problem' components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT headers: SetCookie: description: >- Session cookie `platform_rt` (HttpOnly, Secure, SameSite=Strict, Path=/api/platform/v1/auth). schema: type: string parameters: SessionCookie: name: platform_rt in: cookie required: false schema: type: string maxLength: 128 InvitationToken: name: token in: path required: true schema: type: string minLength: 20 maxLength: 128 OrgID: name: orgID in: path required: true schema: type: string format: uuid UserID: name: userID in: path required: true schema: type: string format: uuid InvitationID: name: invitationID in: path required: true schema: type: string format: uuid SessionID: name: sessionID in: path required: true schema: type: string format: uuid ProductID: name: productID in: path required: true schema: $ref: '#/components/schemas/ProductID' Page: name: page in: query schema: type: integer minimum: 1 maximum: 100000 default: 1 PageSize: name: page_size in: query schema: type: integer minimum: 1 maximum: 200 default: 50 Query: name: q in: query schema: type: string maxLength: 200 responses: Problem: description: Error (RFC 7807). content: application/problem+json: schema: $ref: '#/components/schemas/Problem' SessionWithCookie: description: Signed in. The refresh token is set as the `platform_rt` cookie. headers: Set-Cookie: $ref: '#/components/headers/SetCookie' content: application/json: schema: $ref: '#/components/schemas/AuthSession' schemas: Health: type: object required: - status properties: status: type: string enum: - ok Problem: type: object description: RFC 7807 problem details. required: - type - title - status properties: type: type: string description: URI reference identifying the problem type. example: about:blank title: type: string status: type: integer detail: type: string code: type: string description: Stable machine-readable error code, e.g. totp_required. instance: type: string request_id: type: string fields: type: object description: Per-field validation errors. additionalProperties: type: string Jwks: type: object required: - keys properties: keys: type: array items: $ref: '#/components/schemas/Jwk' Jwk: type: object required: - kty - crv - x - kid - alg - use properties: kty: type: string enum: - OKP crv: type: string enum: - Ed25519 x: type: string description: Base64url public key. kid: type: string alg: type: string enum: - EdDSA use: type: string enum: - sig UserStatus: type: string enum: - active - disabled - deleted OrgRole: type: string enum: - admin - member description: '`admin` manages the organization''s members and their product roles.' OrgStatus: type: string enum: - active - suspended ProductID: type: string pattern: ^[a-z][a-z0-9-]{1,30}$ example: rmm User: type: object required: - id - email - display_name - status - is_platform_admin - totp_enabled - totp_required - last_login_at - created_at properties: id: type: string format: uuid email: type: string display_name: type: string status: $ref: '#/components/schemas/UserStatus' is_platform_admin: type: boolean description: Manages the platform; global admin in every product. totp_enabled: type: boolean totp_required: type: boolean description: An organization of the user requires two-factor authentication. last_login_at: type: string format: date-time nullable: true created_at: type: string format: date-time AuthSession: type: object required: - access_token - expires_in - user properties: access_token: type: string expires_in: type: integer description: Seconds until the access token expires. user: $ref: '#/components/schemas/User' LoginRequest: type: object required: - email - password additionalProperties: false properties: email: type: string minLength: 3 maxLength: 254 password: type: string minLength: 1 maxLength: 256 totp_code: type: string maxLength: 16 description: 6-digit code or a recovery code. ProductTokenRequest: type: object required: - product additionalProperties: false properties: product: $ref: '#/components/schemas/ProductID' StepUpRequest: type: object required: - product - password additionalProperties: false properties: product: $ref: '#/components/schemas/ProductID' password: type: string minLength: 1 maxLength: 256 ProductToken: type: object required: - token - expires_in properties: token: type: string expires_in: type: integer description: Seconds until the token expires. ForgotPasswordRequest: type: object required: - email additionalProperties: false properties: email: type: string minLength: 3 maxLength: 254 ResetPasswordRequest: type: object required: - token - new_password additionalProperties: false properties: token: type: string minLength: 20 maxLength: 128 new_password: type: string minLength: 1 maxLength: 256 ChangePasswordRequest: type: object additionalProperties: false required: - current_password - new_password properties: current_password: type: string minLength: 1 maxLength: 256 new_password: type: string minLength: 1 maxLength: 256 RequestEmailChangeRequest: type: object additionalProperties: false required: - current_password - new_email properties: current_password: type: string minLength: 1 maxLength: 256 new_email: type: string minLength: 3 maxLength: 254 ConfirmEmailChangeRequest: type: object additionalProperties: false required: - token properties: token: type: string minLength: 20 maxLength: 128 TotpSetup: type: object required: - secret - otpauth_url - qr_svg properties: secret: type: string otpauth_url: type: string qr_svg: type: string description: SVG markup of the QR code. TotpConfirmRequest: type: object required: - code additionalProperties: false properties: code: type: string minLength: 6 maxLength: 6 pattern: ^[0-9]{6}$ RecoveryCodes: type: object required: - recovery_codes properties: recovery_codes: type: array items: type: string PasswordConfirmRequest: type: object required: - password additionalProperties: false properties: password: type: string minLength: 1 maxLength: 256 UpdateMeRequest: type: object required: - display_name additionalProperties: false properties: display_name: type: string minLength: 1 maxLength: 200 Session: type: object required: - id - user_agent - ip - created_at - last_used_at - current properties: id: type: string format: uuid description: Session id (the `sid` of its tokens). user_agent: type: string ip: type: string created_at: type: string format: date-time last_used_at: type: string format: date-time current: type: boolean description: The session making this request. SessionList: type: object required: - items properties: items: type: array items: $ref: '#/components/schemas/Session' Product: type: object required: - id - name - base_path - roles - enabled - beta properties: id: $ref: '#/components/schemas/ProductID' name: type: string example: Entrosity Axis base_path: type: string example: /axis description: Where the product is served on this host. roles: type: array description: Roles a member can have in the product, most privileged first. items: type: string enabled: type: boolean beta: type: boolean description: | In beta: open to platform admins only. The Hub issues its product tokens to platform admins only and lists it to nobody else. UpdateProductRequest: type: object additionalProperties: false required: - beta properties: beta: type: boolean description: true puts the product in beta (platform admins only). ProductList: type: object required: - items properties: items: type: array items: $ref: '#/components/schemas/Product' OrganizationRef: type: object required: - id - name - slug - status properties: id: type: string format: uuid name: type: string slug: type: string status: $ref: '#/components/schemas/OrgStatus' OrganizationSettings: type: object additionalProperties: false properties: require_totp: type: boolean description: >- Members must enable two-factor authentication before opening a product. Organization: type: object required: - id - name - slug - status - settings - products - member_count - created_at - updated_at properties: id: type: string format: uuid name: type: string slug: type: string status: $ref: '#/components/schemas/OrgStatus' settings: $ref: '#/components/schemas/OrganizationSettings' products: type: array description: Products enabled for the organization. items: $ref: '#/components/schemas/ProductID' member_count: type: integer created_at: type: string format: date-time updated_at: type: string format: date-time OrganizationList: type: object required: - items - page - page_size - total properties: items: type: array items: $ref: '#/components/schemas/Organization' page: type: integer page_size: type: integer total: type: integer CreateOrganizationRequest: type: object required: - name - slug additionalProperties: false properties: name: type: string minLength: 1 maxLength: 200 slug: type: string pattern: ^[a-z0-9-]{3,40}$ products: type: array maxItems: 20 description: Products to enable. items: $ref: '#/components/schemas/ProductID' settings: $ref: '#/components/schemas/OrganizationSettings' admin_email: type: string minLength: 3 maxLength: 254 description: >- Invite this address as the first organization admin, with the most privileged role in every enabled product. UpdateOrganizationRequest: type: object additionalProperties: false properties: name: type: string minLength: 1 maxLength: 200 status: $ref: '#/components/schemas/OrgStatus' settings: $ref: '#/components/schemas/OrganizationSettings' ProductRole: type: object required: - product - role additionalProperties: false properties: product: $ref: '#/components/schemas/ProductID' role: type: string minLength: 1 maxLength: 50 example: technician LauncherEntry: type: object description: | One card of the launcher. `organization` is missing for the platform-admin entry that opens the product's administration. required: - product - product_name - role - url - beta properties: product: $ref: '#/components/schemas/ProductID' product_name: type: string beta: type: boolean description: The product is in beta (only platform admins see such entries). organization: $ref: '#/components/schemas/OrganizationRef' role: type: string description: >- The user's role in the product for this organization (`global_admin` for platform admins). url: type: string description: Where to open it, for example `/axis/t/`. LauncherEntryList: type: object required: - items properties: items: type: array items: $ref: '#/components/schemas/LauncherEntry' Membership: type: object required: - organization - org_role - product_roles properties: organization: $ref: '#/components/schemas/OrganizationRef' org_role: $ref: '#/components/schemas/OrgRole' product_roles: type: array items: $ref: '#/components/schemas/ProductRole' MembershipList: type: object required: - items properties: items: type: array items: $ref: '#/components/schemas/Membership' MemberUser: type: object required: - id - email - display_name - status - totp_enabled - last_login_at properties: id: type: string format: uuid email: type: string display_name: type: string status: $ref: '#/components/schemas/UserStatus' totp_enabled: type: boolean last_login_at: type: string format: date-time nullable: true Member: type: object required: - user - org_role - product_roles - joined_at properties: user: $ref: '#/components/schemas/MemberUser' org_role: $ref: '#/components/schemas/OrgRole' product_roles: type: array items: $ref: '#/components/schemas/ProductRole' joined_at: type: string format: date-time MemberDirectory: type: object required: - members - invitations properties: members: type: array items: $ref: '#/components/schemas/Member' invitations: type: array items: $ref: '#/components/schemas/Invitation' UpdateMemberRequest: type: object additionalProperties: false properties: org_role: $ref: '#/components/schemas/OrgRole' product_roles: type: array maxItems: 20 items: $ref: '#/components/schemas/ProductRole' InviteMemberRequest: type: object required: - email - org_role - product_roles additionalProperties: false properties: email: type: string minLength: 3 maxLength: 254 org_role: $ref: '#/components/schemas/OrgRole' product_roles: type: array maxItems: 20 items: $ref: '#/components/schemas/ProductRole' Invitation: type: object description: | `organization` and `org_role` are missing for an invitation that only makes the invitee a platform admin. required: - id - email - product_roles - platform_admin - invited_by - expires_at - created_at properties: id: type: string format: uuid email: type: string organization: $ref: '#/components/schemas/OrganizationRef' org_role: $ref: '#/components/schemas/OrgRole' product_roles: type: array items: $ref: '#/components/schemas/ProductRole' platform_admin: type: boolean invited_by: type: string format: uuid nullable: true expires_at: type: string format: date-time created_at: type: string format: date-time InvitationList: type: object required: - items properties: items: type: array items: $ref: '#/components/schemas/Invitation' InvitationInfo: type: object required: - email - organization_name - platform_admin - existing_account - expires_at properties: email: type: string organization_name: type: string nullable: true platform_admin: type: boolean existing_account: type: boolean description: >- The address already has an account; sign in to accept instead of choosing a password. expires_at: type: string format: date-time AcceptInvitationRequest: type: object additionalProperties: false properties: display_name: type: string minLength: 1 maxLength: 200 password: type: string minLength: 1 maxLength: 256 CreateInvitationRequest: type: object required: - email additionalProperties: false properties: email: type: string minLength: 3 maxLength: 254 platform_admin: type: boolean default: false organization_id: type: string format: uuid org_role: $ref: '#/components/schemas/OrgRole' product_roles: type: array maxItems: 20 items: $ref: '#/components/schemas/ProductRole' AdminUser: type: object required: - id - email - display_name - status - is_platform_admin - totp_enabled - last_login_at - created_at - memberships properties: id: type: string format: uuid email: type: string display_name: type: string status: $ref: '#/components/schemas/UserStatus' is_platform_admin: type: boolean totp_enabled: type: boolean last_login_at: type: string format: date-time nullable: true created_at: type: string format: date-time memberships: type: array items: $ref: '#/components/schemas/Membership' AdminUserList: type: object required: - items - page - page_size - total properties: items: type: array items: $ref: '#/components/schemas/AdminUser' page: type: integer page_size: type: integer total: type: integer UpdateUserRequest: type: object additionalProperties: false properties: display_name: type: string minLength: 1 maxLength: 200 status: type: string enum: - active - disabled is_platform_admin: type: boolean ResetUserAuthRequest: type: object additionalProperties: false properties: two_factor: type: boolean default: false description: Turn two-factor authentication off. password: type: boolean default: false description: Replace the password and e-mail a link to choose a new one. AccessEntry: type: object required: - organization_id - org_role - product_roles additionalProperties: false properties: organization_id: type: string format: uuid org_role: $ref: '#/components/schemas/OrgRole' product_roles: type: array maxItems: 20 items: $ref: '#/components/schemas/ProductRole' SetUserAccessRequest: type: object required: - memberships additionalProperties: false properties: memberships: type: array maxItems: 500 items: $ref: '#/components/schemas/AccessEntry' AdminOverview: type: object required: - users_total - users_active - platform_admins - organizations_total - organizations_active - invitations_open - products properties: users_total: type: integer users_active: type: integer platform_admins: type: integer organizations_total: type: integer organizations_active: type: integer invitations_open: type: integer products: type: array description: Organizations and users with access, per product. items: type: object required: - product - organizations - users properties: product: $ref: '#/components/schemas/ProductID' organizations: type: integer users: type: integer AuditEntry: type: object required: - id - ts - organization_id - actor_user_id - actor_email - action - resource_type - resource_id - before - after - ip - request_id properties: id: type: string format: uuid ts: type: string format: date-time organization_id: type: string format: uuid nullable: true actor_user_id: type: string format: uuid nullable: true actor_email: type: string action: type: string resource_type: type: string resource_id: type: string before: type: object nullable: true additionalProperties: true after: type: object nullable: true additionalProperties: true ip: type: string request_id: type: string AuditList: type: object required: - items - page - page_size - total properties: items: type: array items: $ref: '#/components/schemas/AuditEntry' page: type: integer page_size: type: integer total: type: integer