openapi: 3.0.3 info: title: Malphite API version: v1 description: | OpenAPI specification for the current `/api/v1` endpoints. Authentication uses Bearer tokens in the format `:`. servers: - url: /api/v1 security: - BearerAuth: [] tags: - name: auth description: Public authentication endpoints (no Bearer token required) - name: me - name: tokens - name: users - name: tenants - name: departments - name: roles - name: permissions - name: settings paths: /auth/login: post: tags: [auth] summary: Login with email and password description: | Authenticates a user with email and password and returns a Bearer token. This endpoint is **public** — no Authorization header required. The returned token can be used immediately as `Authorization: Bearer ` for all subsequent API requests. Rate limits (stricter than general API limits): - 10 failed attempts per IP per 10 minutes - 5 failed attempts per email+IP per 15 minutes security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LoginRequest' example: email: user@example.com password: "..." token_name: "My App" responses: '201': description: Login successful — token issued content: application/json: schema: $ref: '#/components/schemas/LoginResponse' '401': description: Authentication failed content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' examples: invalid_credentials: value: ok: false request_id: 550e8400-e29b-41d4-a716-446655440000 error_code: invalid_credentials details: {} '403': $ref: '#/components/responses/Forbidden' '409': $ref: '#/components/responses/Conflict' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' /me: get: tags: [me] summary: Get authenticated user profile responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/MeResponse' '401': $ref: '#/components/responses/Unauthorized' '429': $ref: '#/components/responses/RateLimited' put: tags: [me] summary: Update authenticated user profile requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MeUpdateRequest' responses: '200': description: Updated profile content: application/json: schema: $ref: '#/components/schemas/MeResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' patch: tags: [me] summary: Patch authenticated user profile requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MeUpdateRequest' responses: '200': description: Updated profile content: application/json: schema: $ref: '#/components/schemas/MeResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' /me/password: post: tags: [me] summary: Change password for authenticated user requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MePasswordRequest' responses: '204': description: No Content '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' /me/avatar: get: tags: [me] summary: Get avatar image for authenticated user parameters: - $ref: '#/components/parameters/AvatarSize' responses: '200': description: Avatar binary content: image/jpeg: {} image/png: {} image/webp: {} '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' post: tags: [me] summary: Upload avatar for authenticated user requestBody: required: true content: multipart/form-data: schema: type: object required: [avatar] properties: avatar: type: string format: binary responses: '201': $ref: '#/components/responses/DataObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' delete: tags: [me] summary: Delete avatar for authenticated user responses: '204': description: No Content '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '429': $ref: '#/components/responses/RateLimited' /me/tokens: get: tags: [tokens] summary: List current user API tokens parameters: - $ref: '#/components/parameters/Limit50' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/TokenListResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '429': $ref: '#/components/responses/RateLimited' post: tags: [tokens] summary: Create API token for current user requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TokenCreateRequest' responses: '201': description: Created content: application/json: schema: $ref: '#/components/schemas/TokenCreateResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '409': $ref: '#/components/responses/Conflict' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' /me/tokens/show/{uuid}: parameters: - $ref: '#/components/parameters/UuidPath' get: tags: [tokens] summary: Get current user token by UUID responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/TokenShowResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' delete: tags: [tokens] summary: Revoke current user token by UUID responses: '204': description: No Content '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/ServerError' '429': $ref: '#/components/responses/RateLimited' /me/tokens/rotate: post: tags: [tokens] summary: Rotate currently used API token requestBody: required: false content: application/json: schema: type: object properties: expires_at: type: string format: date-time responses: '201': description: Created content: application/json: schema: $ref: '#/components/schemas/TokenCreateResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' /me/tokens/revoke-all: post: tags: [tokens] summary: Revoke all current user API tokens responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/RevokeAllResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '429': $ref: '#/components/responses/RateLimited' /users: get: tags: [users] summary: List users parameters: - $ref: '#/components/parameters/Limit25' - $ref: '#/components/parameters/Offset' - $ref: '#/components/parameters/Search' - $ref: '#/components/parameters/OrderUsers' - $ref: '#/components/parameters/Dir' - $ref: '#/components/parameters/ActiveFilter' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/UserListResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '429': $ref: '#/components/responses/RateLimited' post: tags: [users] summary: Create user requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UserCreateRequest' responses: '201': $ref: '#/components/responses/DataObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' /users/show/{uuid}: parameters: - $ref: '#/components/parameters/UuidPath' get: tags: [users] summary: Get user by UUID responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/UserShowResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' put: tags: [users] summary: Update user by UUID requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UserUpdateRequest' responses: '200': $ref: '#/components/responses/DataObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' patch: tags: [users] summary: Patch user by UUID requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UserUpdateRequest' responses: '200': $ref: '#/components/responses/DataObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' delete: tags: [users] summary: Delete user by UUID responses: '200': $ref: '#/components/responses/DataObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' /users/avatar/{uuid}: parameters: - $ref: '#/components/parameters/UuidPath' - $ref: '#/components/parameters/AvatarSize' get: tags: [users] summary: Get avatar image by user UUID responses: '200': description: Avatar binary content: image/jpeg: {} image/png: {} image/webp: {} '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' /tenants: get: tags: [tenants] summary: List tenants parameters: - $ref: '#/components/parameters/Limit25' - $ref: '#/components/parameters/Offset' - $ref: '#/components/parameters/Search' - $ref: '#/components/parameters/OrderTenants' - $ref: '#/components/parameters/Dir' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/TenantListResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '429': $ref: '#/components/responses/RateLimited' post: tags: [tenants] summary: Create tenant requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TenantWriteRequest' responses: '201': $ref: '#/components/responses/DataObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' /tenants/show/{uuid}: parameters: - $ref: '#/components/parameters/UuidPath' get: tags: [tenants] summary: Get tenant by UUID responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/TenantShowResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' put: tags: [tenants] summary: Update tenant by UUID requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TenantWriteRequest' responses: '200': $ref: '#/components/responses/DataObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' patch: tags: [tenants] summary: Patch tenant by UUID requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TenantWriteRequest' responses: '200': $ref: '#/components/responses/DataObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' delete: tags: [tenants] summary: Delete tenant by UUID responses: '200': $ref: '#/components/responses/DataObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' /tenants/avatar/{uuid}: parameters: - $ref: '#/components/parameters/UuidPath' - $ref: '#/components/parameters/AvatarSize' get: tags: [tenants] summary: Get avatar image by tenant UUID responses: '200': description: Avatar binary content: image/jpeg: {} image/png: {} image/webp: {} '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' /departments: get: tags: [departments] summary: List departments parameters: - $ref: '#/components/parameters/Limit25' - $ref: '#/components/parameters/Offset' - $ref: '#/components/parameters/Search' - $ref: '#/components/parameters/OrderDepartments' - $ref: '#/components/parameters/Dir' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/DepartmentListResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '429': $ref: '#/components/responses/RateLimited' post: tags: [departments] summary: Create department requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DepartmentWriteRequest' responses: '201': $ref: '#/components/responses/DataObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' /departments/show/{uuid}: parameters: - $ref: '#/components/parameters/UuidPath' get: tags: [departments] summary: Get department by UUID responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/DepartmentShowResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' put: tags: [departments] summary: Update department by UUID requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DepartmentWriteRequest' responses: '200': $ref: '#/components/responses/DataObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' patch: tags: [departments] summary: Patch department by UUID requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DepartmentWriteRequest' responses: '200': $ref: '#/components/responses/DataObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' delete: tags: [departments] summary: Delete department by UUID responses: '200': $ref: '#/components/responses/DataObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' /roles: get: tags: [roles] summary: List roles parameters: - $ref: '#/components/parameters/Limit25' - $ref: '#/components/parameters/Offset' - $ref: '#/components/parameters/Search' - $ref: '#/components/parameters/OrderRoles' - $ref: '#/components/parameters/Dir' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/RoleListResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '429': $ref: '#/components/responses/RateLimited' post: tags: [roles] summary: Create role requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RoleWriteRequest' responses: '201': $ref: '#/components/responses/DataObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' /roles/show/{uuid}: parameters: - $ref: '#/components/parameters/UuidPath' get: tags: [roles] summary: Get role by UUID responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/RoleShowResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' put: tags: [roles] summary: Update role by UUID requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RoleWriteRequest' responses: '200': $ref: '#/components/responses/DataObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' patch: tags: [roles] summary: Patch role by UUID requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RoleWriteRequest' responses: '200': $ref: '#/components/responses/DataObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' delete: tags: [roles] summary: Delete role by UUID responses: '200': $ref: '#/components/responses/DataObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' /permissions: get: tags: [permissions] summary: List permissions parameters: - $ref: '#/components/parameters/Limit25' - $ref: '#/components/parameters/Offset' - $ref: '#/components/parameters/Search' - $ref: '#/components/parameters/OrderPermissions' - $ref: '#/components/parameters/Dir' - $ref: '#/components/parameters/ActiveFilter' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/PermissionListResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '429': $ref: '#/components/responses/RateLimited' /settings: get: tags: [settings] summary: Get application settings snapshot responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/SettingsResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '429': $ref: '#/components/responses/RateLimited' put: tags: [settings] summary: Update application settings requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SettingsUpdateRequest' responses: '200': description: Updated settings snapshot content: application/json: schema: $ref: '#/components/schemas/SettingsResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' patch: tags: [settings] summary: Patch application settings requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SettingsUpdateRequest' responses: '200': description: Updated settings snapshot content: application/json: schema: $ref: '#/components/schemas/SettingsResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/ValidationError' '429': $ref: '#/components/responses/RateLimited' /settings/public: get: tags: [settings] summary: Get public app settings for unauthenticated clients security: [] responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/GenericDataResponse' '429': $ref: '#/components/responses/RateLimited' components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: selector:token parameters: UuidPath: name: uuid in: path required: true schema: type: string format: uuid Limit25: name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 25 Limit50: name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 50 Offset: name: offset in: query schema: type: integer minimum: 0 default: 0 Search: name: search in: query schema: type: string Dir: name: dir in: query schema: type: string enum: [asc, desc] default: asc ActiveFilter: name: active in: query schema: type: string description: "Accepted aliases: 1, 0, true, false, active, inactive" AvatarSize: name: size in: query schema: type: integer minimum: 16 maximum: 1024 OrderUsers: name: order in: query schema: type: string enum: [id, uuid, first_name, last_name, display_name, email, created, modified, active, last_login_at] default: last_name OrderTenants: name: order in: query schema: type: string enum: [id, uuid, description, created, modified] default: description OrderDepartments: name: order in: query schema: type: string enum: [id, uuid, description, code, cost_center, active, created, modified] default: description OrderRoles: name: order in: query schema: type: string enum: [id, uuid, description, code, active, created, modified] default: description OrderPermissions: name: order in: query schema: type: string enum: [key, description, active, created] default: key responses: Unauthorized: description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: ok: false request_id: 550e8400-e29b-41d4-a716-446655440000 error_code: unauthorized details: {} Forbidden: description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' NotFound: description: Not found content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: ok: false request_id: 550e8400-e29b-41d4-a716-446655440000 error_code: not_found details: {} ValidationError: description: Validation failed content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: ok: false request_id: 550e8400-e29b-41d4-a716-446655440000 error_code: validation_error details: errors: field: - invalid_value Conflict: description: Conflict content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' RateLimited: description: Rate limited headers: Retry-After: schema: type: integer content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: ok: false request_id: 550e8400-e29b-41d4-a716-446655440000 error_code: rate_limit_exceeded details: retry_after: 60 ServerError: description: Internal server error content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' DataObject: description: Generic service response content: application/json: schema: $ref: '#/components/schemas/GenericDataResponse' schemas: ErrorResponse: type: object required: [ok, request_id, error_code, details] properties: ok: type: boolean example: false request_id: type: string format: uuid error_code: type: string details: type: object additionalProperties: true GenericDataResponse: type: object required: [data] properties: data: type: object additionalProperties: true LoginRequest: type: object additionalProperties: false required: [email, password] properties: email: type: string format: email description: User email address (case-insensitive) password: type: string description: User password token_name: type: string description: Optional label for the issued token (e.g. "iPhone App"). Defaults to "Login". default: Login tenant_uuid: type: string format: uuid description: Optional — scope the issued token to a specific tenant UUID. Must be assigned to the user. LoginResponse: type: object required: [data] properties: data: type: object required: [token, token_uuid, expires_at] properties: token: type: string description: Full Bearer token in format `selector:plaintext`. Shown only once — store securely. example: "abc123def:456789..." token_uuid: type: string format: uuid description: UUID of the created token record (use for revocation via /me/tokens) expires_at: type: string format: date-time nullable: true description: UTC expiry timestamp of the token MeUpdateRequest: type: object additionalProperties: false properties: first_name: type: string last_name: type: string profile_description: type: string job_title: type: string phone: type: string mobile: type: string short_dial: type: string address: type: string postal_code: type: string city: type: string country: type: string region: type: string locale: type: string theme: type: string current_tenant_uuid: type: string format: uuid MePasswordRequest: type: object additionalProperties: false required: [current_password, password, password2] properties: current_password: type: string password: type: string password2: type: string PermissionListItem: type: object properties: key: type: string description: type: string active: type: boolean is_system: type: boolean PermissionListResponse: type: object properties: data: type: array items: $ref: '#/components/schemas/PermissionListItem' total: type: integer limit: type: integer offset: type: integer SettingsObject: type: object properties: app_title: type: string app_locale: type: string app_registration_enabled: type: boolean api_token_default_ttl_days: type: integer nullable: true api_token_max_ttl_days: type: integer nullable: true api_cors_allowed_origins: type: string default_tenant_id: type: integer nullable: true default_role_id: type: integer nullable: true default_department_id: type: integer nullable: true user_inactivity_deactivate_days: type: integer nullable: true user_inactivity_delete_days: type: integer nullable: true system_audit_enabled: type: boolean system_audit_retention_days: type: integer nullable: true microsoft_shared_client_id: type: string microsoft_authority: type: string smtp_host: type: string smtp_port: type: integer nullable: true smtp_user: type: string smtp_secure: type: string smtp_from: type: string smtp_from_name: type: string SettingsResponse: type: object required: [data] properties: data: $ref: '#/components/schemas/SettingsObject' SettingsUpdateRequest: type: object additionalProperties: false properties: app_title: type: string app_locale: type: string app_registration_enabled: type: boolean api_token_default_ttl_days: type: integer nullable: true api_token_max_ttl_days: type: integer nullable: true api_cors_allowed_origins: type: string default_tenant_id: type: integer nullable: true default_role_id: type: integer nullable: true default_department_id: type: integer nullable: true user_inactivity_deactivate_days: type: integer nullable: true user_inactivity_delete_days: type: integer nullable: true system_audit_enabled: type: boolean system_audit_retention_days: type: integer nullable: true microsoft_shared_client_id: type: string microsoft_authority: type: string smtp_host: type: string smtp_port: type: integer nullable: true smtp_user: type: string smtp_password: type: string smtp_secure: type: string smtp_from: type: string smtp_from_name: type: string RoleWriteRequest: type: object additionalProperties: false required: [description] properties: description: type: string minLength: 1 code: type: string active: type: string description: "Accepted aliases: 1, 0, true, false, active, inactive" DepartmentWriteRequest: type: object additionalProperties: false required: [description, tenant_uuid] properties: description: type: string minLength: 1 tenant_uuid: type: string format: uuid code: type: string cost_center: type: string active: type: string description: "Accepted aliases: 1, 0, true, false, active, inactive" TenantWriteRequest: type: object additionalProperties: false required: [description, status] properties: description: type: string minLength: 1 status: type: string enum: [active, inactive] address: type: string postal_code: type: string city: type: string country: type: string region: type: string vat_id: type: string tax_number: type: string phone: type: string fax: type: string email: type: string format: email support_email: type: string format: email support_phone: type: string billing_email: type: string format: email website: type: string privacy_url: type: string imprint_url: type: string primary_color: type: string description: "Hex color, e.g. #0f766e" primary_color_use_default: type: boolean default_theme: type: string allow_user_theme_mode: type: string enum: ['', '0', '1'] UserCreateRequest: type: object additionalProperties: false required: [first_name, last_name, email, password, password2] properties: first_name: type: string minLength: 1 last_name: type: string minLength: 1 email: type: string format: email profile_description: type: string job_title: type: string phone: type: string mobile: type: string short_dial: type: string address: type: string postal_code: type: string city: type: string country: type: string region: type: string hire_date: type: string format: date locale: type: string theme: type: string totp_secret: type: string active: type: boolean password: type: string password2: type: string tenant_uuids: type: array items: type: string format: uuid primary_tenant_uuid: type: string format: uuid role_uuids: type: array items: type: string format: uuid department_uuids: type: array items: type: string format: uuid UserUpdateRequest: type: object additionalProperties: false required: [first_name, last_name, email] properties: first_name: type: string minLength: 1 last_name: type: string minLength: 1 email: type: string format: email profile_description: type: string job_title: type: string phone: type: string mobile: type: string short_dial: type: string address: type: string postal_code: type: string city: type: string country: type: string region: type: string hire_date: type: string format: date locale: type: string theme: type: string totp_secret: type: string active: type: boolean password: type: string password2: type: string tenant_uuids: type: array items: type: string format: uuid primary_tenant_uuid: type: string format: uuid role_uuids: type: array items: type: string format: uuid department_uuids: type: array items: type: string format: uuid TenantSummary: type: object properties: uuid: type: string format: uuid description: type: string status: type: string AssignmentDepartment: type: object properties: uuid: type: string format: uuid description: type: string active: type: boolean tenant_uuid: type: string format: uuid nullable: true AssignmentRole: type: object properties: uuid: type: string format: uuid description: type: string active: type: boolean Assignments: type: object properties: tenants: type: array items: $ref: '#/components/schemas/TenantSummary' departments: type: array items: $ref: '#/components/schemas/AssignmentDepartment' roles: type: array items: $ref: '#/components/schemas/AssignmentRole' UserCustomFieldOption: type: object properties: option_key: type: string label: type: string UserCustomFieldField: type: object properties: uuid: type: string format: uuid field_key: type: string label: type: string type: type: string is_filterable: type: boolean value: description: Type-dependent value (string, boolean, null, or array of option keys). options: type: array items: $ref: '#/components/schemas/UserCustomFieldOption' UserCustomFieldTenantGroup: type: object properties: tenant: $ref: '#/components/schemas/TenantSummary' fields: type: array items: $ref: '#/components/schemas/UserCustomFieldField' MeResponse: type: object required: [data] properties: data: type: object properties: uuid: type: string format: uuid first_name: type: string last_name: type: string display_name: type: string email: type: string job_title: type: string phone: type: string mobile: type: string locale: type: string current_tenant: $ref: '#/components/schemas/TenantSummary' primary_tenant: $ref: '#/components/schemas/TenantSummary' can_view_permissions: type: boolean permissions: type: array items: type: string assignments: $ref: '#/components/schemas/Assignments' custom_fields: type: array items: $ref: '#/components/schemas/UserCustomFieldTenantGroup' TokenItem: type: object properties: uuid: type: string format: uuid name: type: string selector_prefix: type: string tenant_uuid: type: string format: uuid nullable: true last_used_at: type: string nullable: true last_ip: type: string expires_at: type: string nullable: true revoked_at: type: string nullable: true created: type: string nullable: true is_current_token: type: boolean TokenListResponse: type: object required: [data] properties: data: type: array items: $ref: '#/components/schemas/TokenItem' limit: type: integer TokenShowResponse: type: object required: [data] properties: data: $ref: '#/components/schemas/TokenItem' TokenCreateRequest: type: object required: [name] properties: name: type: string tenant_uuid: type: string format: uuid expires_at: type: string format: date-time TokenCreateResponse: type: object required: [data] properties: data: type: object properties: token_uuid: type: string format: uuid token: type: string selector_prefix: type: string tenant_uuid: type: string format: uuid nullable: true expires_at: type: string nullable: true RevokeAllResponse: type: object required: [data] properties: data: type: object properties: revoked_count: type: integer tenant_uuid: type: string format: uuid nullable: true UserListItem: type: object properties: uuid: type: string format: uuid first_name: type: string last_name: type: string display_name: type: string email: type: string job_title: type: string phone: type: string mobile: type: string active: type: boolean created: type: string tenants: type: array items: type: string departments: type: array items: type: string roles: type: array items: type: string UserListResponse: type: object properties: data: type: array items: $ref: '#/components/schemas/UserListItem' total: type: integer limit: type: integer offset: type: integer UserShowResponse: type: object properties: data: type: object properties: uuid: type: string format: uuid first_name: type: string last_name: type: string display_name: type: string email: type: string profile_description: type: string job_title: type: string phone: type: string mobile: type: string short_dial: type: string address: type: string postal_code: type: string city: type: string country: type: string region: type: string hire_date: type: string format: date locale: type: string theme: type: string email_verified_at: type: string nullable: true last_login_at: type: string nullable: true last_login_provider: type: string nullable: true active: type: boolean created: type: string modified: type: string current_tenant: $ref: '#/components/schemas/TenantSummary' primary_tenant: $ref: '#/components/schemas/TenantSummary' permissions: type: array items: type: string assignments: $ref: '#/components/schemas/Assignments' custom_fields: type: array items: $ref: '#/components/schemas/UserCustomFieldTenantGroup' TenantListItem: type: object properties: uuid: type: string format: uuid description: type: string status: type: string city: type: string country: type: string created: type: string TenantListResponse: type: object properties: data: type: array items: $ref: '#/components/schemas/TenantListItem' total: type: integer limit: type: integer offset: type: integer TenantShowResponse: type: object properties: data: type: object properties: uuid: type: string format: uuid description: type: string status: type: string address: type: string postal_code: type: string city: type: string country: type: string region: type: string vat_id: type: string tax_number: type: string phone: type: string fax: type: string email: type: string support_email: type: string support_phone: type: string billing_email: type: string website: type: string privacy_url: type: string imprint_url: type: string primary_color: type: string primary_color_use_default: type: boolean default_theme: type: string allow_user_theme_mode: type: string enum: ['', '0', '1'] created: type: string modified: type: string DepartmentListItem: type: object properties: uuid: type: string format: uuid description: type: string code: type: string active: type: boolean tenant_labels: type: array items: type: string created: type: string DepartmentListResponse: type: object properties: data: type: array items: $ref: '#/components/schemas/DepartmentListItem' total: type: integer limit: type: integer offset: type: integer DepartmentShowResponse: type: object properties: data: type: object properties: uuid: type: string format: uuid description: type: string code: type: string cost_center: type: string active: type: boolean tenant_uuid: type: string format: uuid created: type: string modified: type: string RoleListItem: type: object properties: uuid: type: string format: uuid description: type: string code: type: string active: type: boolean created: type: string RoleListResponse: type: object properties: data: type: array items: $ref: '#/components/schemas/RoleListItem' total: type: integer limit: type: integer offset: type: integer RoleShowResponse: type: object properties: data: type: object properties: uuid: type: string format: uuid description: type: string code: type: string active: type: boolean permission_keys: type: array items: type: string created: type: string modified: type: string