openapi: 3.1.0 info: title: LandveX Enterprise Platform Admin API version: 1.0.0 description: Admin backend API for LandveX Enterprise Platform servers: - url: https://api.landvex.com/v1 description: Production - url: https://api-staging.landvex.com/v1 description: Staging security: - BearerAuth: [] - ApiKeyAuth: [] paths: # Auth /auth/login: post: tags: [Auth] summary: Login with email/password security: [] requestBody: required: true content: application/json: schema: type: object required: [email, password] properties: email: { type: string, format: email } password: { type: string, minLength: 8 } mfa_code: { type: string } responses: '200': description: Login successful content: application/json: schema: type: object properties: access_token: { type: string } refresh_token: { type: string } token_type: { type: string, default: Bearer } expires_in: { type: integer } mfa_required: { type: boolean } /auth/refresh: post: tags: [Auth] summary: Refresh access token requestBody: required: true content: application/json: schema: type: object required: [refresh_token] properties: refresh_token: { type: string } responses: '200': description: New tokens issued # Tenants /tenants: get: tags: [Tenants] summary: List tenants parameters: - name: page in: query schema: { type: integer, default: 1 } - name: per_page in: query schema: { type: integer, default: 20 } - name: type in: query schema: { type: string, enum: [municipality, enterprise, partner] } - name: status in: query schema: { type: string, enum: [active, suspended, cancelled, trial] } - name: search in: query schema: { type: string } responses: '200': description: List of tenants content: application/json: schema: type: object properties: data: type: array items: { $ref: '#/components/schemas/Tenant' } meta: { $ref: '#/components/schemas/PaginationMeta' } post: tags: [Tenants] summary: Create tenant requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/TenantCreate' } responses: '201': description: Tenant created content: application/json: schema: { $ref: '#/components/schemas/Tenant' } /tenants/{id}: parameters: - name: id in: path required: true schema: { type: string, format: uuid } get: tags: [Tenants] summary: Get tenant details responses: '200': description: Tenant details content: application/json: schema: { $ref: '#/components/schemas/Tenant' } patch: tags: [Tenants] summary: Update tenant requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/TenantUpdate' } responses: '200': description: Tenant updated delete: tags: [Tenants] summary: Delete tenant (soft) responses: '204': description: Tenant deleted # Users /users: get: tags: [Users] summary: List users parameters: - name: tenant_id in: header required: true schema: { type: string, format: uuid } - name: page in: query schema: { type: integer, default: 1 } - name: role in: query schema: { type: string } - name: status in: query schema: { type: string } responses: '200': description: List of users content: application/json: schema: type: object properties: data: type: array items: { $ref: '#/components/schemas/User' } meta: { $ref: '#/components/schemas/PaginationMeta' } post: tags: [Users] summary: Create/invite user requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/UserCreate' } responses: '201': description: User created /users/{id}: parameters: - name: id in: path required: true schema: { type: string, format: uuid } get: tags: [Users] summary: Get user responses: '200': description: User details content: application/json: schema: { $ref: '#/components/schemas/User' } patch: tags: [Users] summary: Update user requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/UserUpdate' } responses: '200': description: User updated delete: tags: [Users] summary: Delete user responses: '204': description: User deleted /users/{id}/roles: parameters: - name: id in: path required: true schema: { type: string, format: uuid } post: tags: [Users] summary: Assign role to user requestBody: required: true content: application/json: schema: type: object required: [role_id] properties: role_id: { type: string, format: uuid } responses: '200': description: Role assigned # Roles /roles: get: tags: [Roles] summary: List roles responses: '200': description: List of roles content: application/json: schema: type: object properties: data: type: array items: { $ref: '#/components/schemas/Role' } post: tags: [Roles] summary: Create role requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/RoleCreate' } responses: '201': description: Role created # API Keys /api-keys: get: tags: [API Keys] summary: List API keys responses: '200': description: List of API keys content: application/json: schema: type: object properties: data: type: array items: { $ref: '#/components/schemas/ApiKey' } post: tags: [API Keys] summary: Generate API key requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/ApiKeyCreate' } responses: '201': description: API key generated (key shown once) content: application/json: schema: type: object properties: id: { type: string, format: uuid } name: { type: string } key: { type: string } # Shown only once! prefix: { type: string } scopes: { type: array, items: { type: string } } created_at: { type: string, format: date-time } /api-keys/{id}/revoke: parameters: - name: id in: path required: true schema: { type: string, format: uuid } post: tags: [API Keys] summary: Revoke API key responses: '200': description: Key revoked # Billing /billing/plans: get: tags: [Billing] summary: List subscription plans responses: '200': description: Available plans content: application/json: schema: type: object properties: data: type: array items: { $ref: '#/components/schemas/SubscriptionPlan' } /billing/subscription: get: tags: [Billing] summary: Get current subscription responses: '200': description: Subscription details content: application/json: schema: { $ref: '#/components/schemas/Subscription' } /billing/invoices: get: tags: [Billing] summary: List invoices parameters: - name: status in: query schema: { type: string } responses: '200': description: List of invoices content: application/json: schema: type: object properties: data: type: array items: { $ref: '#/components/schemas/Invoice' } meta: { $ref: '#/components/schemas/PaginationMeta' } # Audit Logs /audit-logs: get: tags: [Audit Logs] summary: List audit logs parameters: - name: action in: query schema: { type: string } - name: user_id in: query schema: { type: string, format: uuid } - name: resource_type in: query schema: { type: string } - name: from in: query schema: { type: string, format: date-time } - name: to in: query schema: { type: string, format: date-time } - name: severity in: query schema: { type: string, enum: [info, warning, critical] } responses: '200': description: Audit logs content: application/json: schema: type: object properties: data: type: array items: { $ref: '#/components/schemas/AuditLog' } meta: { $ref: '#/components/schemas/PaginationMeta' } /audit-logs/export: get: tags: [Audit Logs] summary: Export audit logs parameters: - name: format in: query schema: { type: string, enum: [csv, json], default: csv } responses: '200': description: Exported file content: text/csv: schema: { type: string } application/json: schema: { type: string } # Reports /reports: get: tags: [Reports] summary: List reports responses: '200': description: List of reports post: tags: [Reports] summary: Create report requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/ReportCreate' } responses: '201': description: Report created /reports/{id}/run: parameters: - name: id in: path required: true schema: { type: string, format: uuid } post: tags: [Reports] summary: Run report responses: '202': description: Report queued # Plugins /plugins: get: tags: [Plugins] summary: List available plugins responses: '200': description: Available plugins content: application/json: schema: type: object properties: data: type: array items: { $ref: '#/components/schemas/Plugin' } /plugins/installed: get: tags: [Plugins] summary: List installed plugins responses: '200': description: Installed plugins /plugins/{id}/install: parameters: - name: id in: path required: true schema: { type: string, format: uuid } post: tags: [Plugins] summary: Install plugin responses: '200': description: Plugin installed # Dashboard /dashboard/summary: get: tags: [Dashboard] summary: Get dashboard summary responses: '200': description: Dashboard statistics content: application/json: schema: { $ref: '#/components/schemas/DashboardSummary' } components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: JWT ApiKeyAuth: type: apiKey in: header name: X-API-Key schemas: PaginationMeta: type: object properties: page: { type: integer } per_page: { type: integer } total: { type: integer } total_pages: { type: integer } Tenant: type: object properties: id: { type: string, format: uuid } name: { type: string } slug: { type: string } type: { type: string, enum: [municipality, enterprise, partner] } org_number: { type: string } billing_email: { type: string, format: email } plan_id: { type: string, format: uuid } status: { type: string } settings: { type: object } created_at: { type: string, format: date-time } updated_at: { type: string, format: date-time } TenantCreate: type: object required: [name, slug, type, billing_email] properties: name: { type: string, minLength: 2, maxLength: 255 } slug: { type: string, pattern: '^[a-z0-9-]+$' } type: { type: string, enum: [municipality, enterprise, partner] } org_number: { type: string } billing_email: { type: string, format: email } plan_id: { type: string, format: uuid } TenantUpdate: type: object properties: name: { type: string } billing_email: { type: string, format: email } status: { type: string } settings: { type: object } User: type: object properties: id: { type: string, format: uuid } tenant_id: { type: string, format: uuid } email: { type: string, format: email } first_name: { type: string } last_name: { type: string } phone: { type: string } avatar_url: { type: string } status: { type: string } roles: { type: array, items: { $ref: '#/components/schemas/Role' } } last_login_at: { type: string, format: date-time } created_at: { type: string, format: date-time } UserCreate: type: object required: [email, first_name, last_name] properties: email: { type: string, format: email } first_name: { type: string } last_name: { type: string } phone: { type: string } role_ids: { type: array, items: { type: string, format: uuid } } send_invite: { type: boolean, default: true } UserUpdate: type: object properties: first_name: { type: string } last_name: { type: string } phone: { type: string } status: { type: string } role_ids: { type: array, items: { type: string, format: uuid } } Role: type: object properties: id: { type: string, format: uuid } name: { type: string } slug: { type: string } description: { type: string } permissions: { type: array, items: { type: string } } is_system: { type: boolean } member_count: { type: integer } created_at: { type: string, format: date-time } RoleCreate: type: object required: [name, slug, permissions] properties: name: { type: string } slug: { type: string } description: { type: string } permissions: { type: array, items: { type: string } } ApiKey: type: object properties: id: { type: string, format: uuid } name: { type: string } prefix: { type: string } scopes: { type: array, items: { type: string } } rate_limit: { type: integer } expires_at: { type: string, format: date-time } last_used_at: { type: string, format: date-time } status: { type: string } created_at: { type: string, format: date-time } ApiKeyCreate: type: object required: [name] properties: name: { type: string } scopes: { type: array, items: { type: string } } rate_limit: { type: integer, default: 1000 } expires_at: { type: string, format: date-time } SubscriptionPlan: type: object properties: id: { type: string, format: uuid } name: { type: string } slug: { type: string } description: { type: string } price_monthly: { type: number } price_yearly: { type: number } features: { type: object } limits: { type: object } Subscription: type: object properties: plan: { $ref: '#/components/schemas/SubscriptionPlan' } status: { type: string } current_period_start: { type: string, format: date-time } current_period_end: { type: string, format: date-time } cancel_at_period_end: { type: boolean } Invoice: type: object properties: id: { type: string, format: uuid } invoice_number: { type: string } period_start: { type: string, format: date } period_end: { type: string, format: date } amount: { type: number } tax_amount: { type: number } total_amount: { type: number } currency: { type: string, default: SEK } status: { type: string } due_date: { type: string, format: date } pdf_url: { type: string } created_at: { type: string, format: date-time } AuditLog: type: object properties: id: { type: string, format: uuid } action: { type: string } resource_type: { type: string } resource_id: { type: string, format: uuid } user: { type: object, properties: { id: { type: string }, email: { type: string } } } details: { type: object } ip_address: { type: string } severity: { type: string } created_at: { type: string, format: date-time } ReportCreate: type: object required: [name, type] properties: name: { type: string } type: { type: string } config: { type: object } schedule: { type: string } Plugin: type: object properties: id: { type: string, format: uuid } name: { type: string } slug: { type: string } description: { type: string } version: { type: string } author: { type: string } is_installed: { type: boolean } is_system: { type: boolean } DashboardSummary: type: object properties: tenants: type: object properties: total: { type: integer } active: { type: integer } trial: { type: integer } users: type: object properties: total: { type: integer } active: { type: integer } api_usage: type: object properties: today: { type: integer } this_month: { type: integer } limit: { type: integer } revenue: type: object properties: mrr: { type: number } this_month: { type: number } last_month: { type: number }