openapi: 3.0.3 info: title: Copenhagen Truck Wash API description: | This API provides access to the Copenhagen Truck Wash system, managing orders, bookings, departments, products, customers, and various integrations including e-conomic, Stripe, XLVask, and more. ## Authentication Most endpoints require authentication using a Bearer token obtained from the `/auth/login` or `/auth/employee/login` endpoints. ## Permissions Many endpoints require specific permissions that are assigned to user groups/roles. version: 1.0.0 contact: name: Copenhagen Truck Wash email: support@truckwash.dk servers: - url: https://api.truckwash.dk description: Production server - url: http://localhost description: Local development server security: - BearerAuth: [] tags: - name: Authentication description: User and employee authentication endpoints - name: Users description: User management and customer operations - name: Orders description: Order creation, management, and retrieval - name: Order Items description: Managing items within orders - name: Bookings description: Booking management for wash services - name: Departments description: Department and location management - name: Products description: Product catalog and pricing - name: Categories description: Product category management - name: Invoices description: Invoice generation and management - name: Payments description: Payment processing and collection - name: Vehicles description: Vehicle registration and management - name: Notifications description: System notifications and alerts - name: Statistics description: Business analytics and reporting - name: Modules description: Third-party integrations and modules - name: Attachments description: File upload and attachment management - name: Forms description: Form submissions and management - name: Worker description: System worker status and maintenance - name: Plate Scans description: License plate scanning operations - name: Config description: Module configuration management - name: Branding description: Branding options management - name: Roles description: Role and permission management - name: Self-Serve description: Self-serve lane operations and questions - name: Goals description: Department goals management paths: # Authentication Endpoints /auth/login: post: tags: - Authentication summary: Customer login description: Authenticate a customer using customer number and password operationId: customerLogin security: [] requestBody: required: true content: application/json: schema: type: object required: - customer_number - password properties: customer_number: type: integer description: Customer's e-conomic customer number example: 12345 password: type: string format: password description: Customer password minLength: 1 responses: '200': description: Login successful content: application/json: schema: type: object properties: token: type: string description: Bearer authentication token example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /auth/employee/login: post: tags: - Authentication summary: Employee login description: Authenticate an employee using user ID and password operationId: employeeLogin security: [] requestBody: required: true content: application/json: schema: type: object required: - user_id - password properties: user_id: type: integer description: Employee user ID example: 1 password: type: string format: password description: Employee password responses: '200': description: Login successful content: application/json: schema: type: object properties: token: type: string description: Bearer authentication token '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /auth/logout: get: tags: - Authentication summary: Logout description: Invalidate the current authentication token operationId: logout responses: '200': description: Logout successful content: application/json: schema: type: object properties: message: type: string example: "Logged out" '401': $ref: '#/components/responses/Unauthorized' /auth/session: get: tags: - Authentication summary: Get current session description: Retrieve information about the current authenticated user session operationId: getSession responses: '200': description: Session information retrieved successfully content: application/json: schema: $ref: '#/components/schemas/User' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /auth/reCAPTCHA/public: get: tags: - Authentication summary: Get reCAPTCHA configuration description: Retrieve public reCAPTCHA configuration for login forms operationId: getRecaptchaConfig security: [] responses: '200': description: reCAPTCHA configuration retrieved successfully content: application/json: schema: type: object properties: rate_limit: type: object properties: enabled: type: boolean limit: type: integer remaining: type: integer reset: type: integer warning: type: string nullable: true recaptcha: type: object /auth/register/cvr: post: tags: - Authentication summary: Register new customer by CVR description: Register a new customer account using Danish CVR number operationId: registerCustomerByCvr security: [] requestBody: required: true content: application/json: schema: type: object required: - cvr - companyPhone - invoiceEmail - contactEmail - contactPhone - contactName properties: cvr: type: string description: Danish CVR number minLength: 8 maxLength: 20 example: "44794780" companyPhone: type: integer description: Company phone number minimum: 10000000 maximum: 9999999999 example: 21754690 invoiceEmail: type: string format: email description: Email for invoices minLength: 5 maxLength: 255 example: "invoice@company.dk" contactEmail: type: string format: email description: Contact email minLength: 5 maxLength: 255 example: "contact@company.dk" contactPhone: type: integer description: Contact phone number minimum: 10000000 maximum: 9999999999 example: 21754690 contactName: type: string description: Contact person name example: "Mikkel" responses: '201': description: Customer registered successfully '400': $ref: '#/components/responses/BadRequest' /auth/password-reset/request: post: tags: - Authentication summary: Request a customer password reset email description: Send an email with a password reset token to the customer's email address operationId: requestPasswordReset security: [] requestBody: required: true content: application/json: schema: type: object required: - customer_number properties: customer_number: type: integer description: The customer number example: 123456 responses: '200': description: Request processed content: application/json: schema: type: object properties: message: type: string '400': $ref: '#/components/responses/BadRequest' /auth/password-reset/validate: get: tags: - Authentication summary: Validate a customer password reset key description: Check if a password reset token is valid and hasn't expired operationId: validatePasswordResetToken security: [] parameters: - name: token in: query required: true schema: type: string description: The password reset token responses: '200': description: Token is valid content: application/json: schema: type: object properties: valid: type: boolean customer_id: type: integer '404': description: Invalid or expired token /auth/password-reset/set: post: tags: - Authentication summary: Set a customer password using a reset key description: Update the customer password using a valid reset token operationId: setPasswordUsingResetToken security: [] requestBody: required: true content: application/json: schema: type: object required: - token - password properties: token: type: string description: The password reset token password: type: string description: The new password responses: '200': description: Password updated successfully content: application/json: schema: type: object properties: message: type: string '400': $ref: '#/components/responses/BadRequest' '404': description: Invalid or expired token /su/intimidate: post: tags: - Authentication summary: Intimidate a user description: Create an authentication token for another user (Superuser only) operationId: suIntimidate requestBody: required: true content: application/json: schema: type: object required: [user_id] properties: user_id: {type: integer} responses: '200': description: Success content: application/json: schema: type: object properties: token: {type: string} # User Endpoints /users: get: tags: - Users summary: List users description: Retrieve a paginated list of users operationId: listUsers parameters: - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' - $ref: '#/components/parameters/SearchParam' responses: '200': description: Users retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/User' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' post: tags: - Users summary: Create new user description: Create a new user account operationId: createUser requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UserCreate' responses: '201': description: User created successfully content: application/json: schema: $ref: '#/components/schemas/User' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' put: tags: - Users summary: Update user description: Update an existing user operationId: updateUser requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UserUpdate' responses: '200': description: User updated successfully '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /users/customer: get: tags: - Users summary: Get customer details description: Get details about a specific customer operationId: getCustomer parameters: - name: customer_number in: query schema: type: integer responses: '200': description: Customer retrieved successfully content: application/json: schema: $ref: '#/components/schemas/User' '404': $ref: '#/components/responses/NotFound' /superuser/user: get: tags: - Users summary: Get user by ID (superuser) description: Get detailed user information by user ID operationId: getSuperuserUser parameters: - name: user_id in: query schema: type: integer responses: '200': description: User retrieved successfully content: application/json: schema: $ref: '#/components/schemas/User' '404': $ref: '#/components/responses/NotFound' /admin/customer/code: get: tags: - Users summary: Get customer code operationId: getCustomerCode parameters: - name: customer_number in: query schema: {type: integer} - name: user_id in: query schema: {type: integer} responses: '200': description: Success post: tags: - Users summary: Add customer code operationId: addCustomerCode requestBody: required: true content: application/json: schema: type: object properties: customer_number: {type: integer} user_id: {type: integer} code: {type: string} responses: '200': description: Success /customer/department/default: get: tags: - Users summary: Get customer default department operationId: getCustomerDefaultDepartment parameters: - name: customer_number in: query schema: {type: integer} responses: '200': description: Success post: tags: - Users summary: Add customer default department operationId: addCustomerDefaultDepartment requestBody: required: true content: application/json: schema: type: object required: [department] properties: customer_number: {type: integer} department: {type: integer} responses: '200': description: Success delete: tags: - Users summary: Delete customer default department operationId: deleteCustomerDefaultDepartment parameters: - name: customer_number in: query schema: {type: integer} responses: '200': description: Success /customer/pricing/fixed: get: tags: - Users summary: Get customer fixed pricing operationId: getCustomerFixedPricing parameters: - name: customer_number in: query required: true schema: {type: integer} responses: '200': description: Success post: tags: - Users summary: Add customer fixed pricing operationId: addCustomerFixedPricing requestBody: required: true content: application/json: schema: type: object required: [customer_number, price, description] properties: customer_number: {type: integer} price: {type: integer} description: {type: string} responses: '200': description: Success delete: tags: - Users summary: Delete customer fixed pricing operationId: deleteCustomerFixedPricing parameters: - name: customer_number in: query required: true schema: {type: integer} responses: '200': description: Success /account/notifications: put: tags: - Users summary: Update user notification settings operationId: updateUserNotifications requestBody: required: true content: application/json: schema: type: object properties: wash_certificate_email: {type: string} sms_notifications_enabled: {type: boolean} email_notifications_enabled: {type: boolean} responses: '200': description: Success /user/permissions: get: tags: - Users summary: Get user permissions operationId: getUserPermissions responses: '200': description: Success /customers: get: tags: - Users summary: List customers operationId: listCustomers parameters: - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' - $ref: '#/components/parameters/SearchParam' responses: '200': description: Success /superuser/user/discounts: get: tags: - Users summary: Get user discounts operationId: getUserDiscounts parameters: - name: user_id in: query required: true schema: {type: integer} responses: '200': description: Success post: tags: - Users summary: Set user discount operationId: setUserDiscount requestBody: required: true content: application/json: schema: type: object required: [discount, object_id, is_category] properties: user_id: {type: integer} discount: {type: integer} object_id: {type: string} is_category: {type: boolean} responses: '200': description: Success /superuser/user/keys: get: tags: - Users summary: Get user keys operationId: getUserKeys parameters: - name: user_id in: query required: true schema: {type: integer} - name: key in: query schema: {type: string} responses: '200': description: Success post: tags: - Users summary: Set user key operationId: setUserKey requestBody: required: true content: application/json: schema: type: object required: [key, value] properties: user_id: {type: integer} key: {type: string} value: {type: string} responses: '200': description: Success /superuser/user/password: post: tags: - Users summary: Set user password operationId: setUserPassword requestBody: required: true content: application/json: schema: type: object required: [password] properties: user_id: {type: integer} password: {type: string} responses: '200': description: Success /admin/customer/getUserId: get: tags: - Users summary: Get user ID from customer number description: Convert e-conomic customer number to internal user ID operationId: getUserIdFromCustomerNumber parameters: - name: customer_number in: query required: true schema: type: integer responses: '200': description: User ID retrieved successfully content: application/json: schema: type: object properties: user_id: type: integer /admin/customer/name: get: tags: - Users summary: Get customer name description: Get the full name of a customer operationId: getCustomerName parameters: - name: user_id in: query schema: type: integer responses: '200': description: Customer name retrieved successfully content: application/json: schema: type: object properties: name: type: string # Orders Endpoints /orders: get: tags: - Orders summary: List orders description: Retrieve a paginated list of orders operationId: listOrders parameters: - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' - $ref: '#/components/parameters/SearchParam' - name: show_wash_subscription in: query schema: type: string enum: [true, false] responses: '200': description: Orders retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/Order' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' post: tags: - Orders summary: Create new order description: Create a new wash order operationId: createOrder requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/OrderCreate' responses: '201': description: Order created successfully content: application/json: schema: $ref: '#/components/schemas/Order' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' delete: tags: - Orders summary: Delete order description: Delete an existing order operationId: deleteOrder parameters: - name: id in: query required: true schema: type: integer responses: '200': description: Order deleted successfully '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' put: tags: - Orders summary: Update order (alias) description: Update an existing order operationId: updateOrders requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/OrderUpdate' responses: '200': description: Order updated successfully '400': $ref: '#/components/responses/BadRequest' /order: get: tags: - Orders summary: Get order details description: Get detailed information about a specific order operationId: getOrder parameters: - name: id in: query required: true schema: type: integer responses: '200': description: Order retrieved successfully content: application/json: schema: $ref: '#/components/schemas/Order' '404': $ref: '#/components/responses/NotFound' put: tags: - Orders summary: Update order description: Update an existing order operationId: updateOrder requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/OrderUpdate' responses: '200': description: Order updated successfully '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /user/orders: get: tags: - Orders summary: Get current user's orders description: Retrieve orders for the authenticated user operationId: getUserOrders parameters: - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' responses: '200': description: Orders retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/Order' /user/order: get: tags: - Orders summary: Get user's specific order description: Get details of a specific order for the authenticated user operationId: getUserOrder parameters: - name: id in: query required: true schema: type: integer responses: '200': description: Order retrieved successfully content: application/json: schema: $ref: '#/components/schemas/Order' /orders/mark_as_completed: post: tags: - Orders summary: Mark order as completed description: Mark an order as completed operationId: markOrderCompleted requestBody: required: true content: application/json: schema: type: object required: - id properties: id: type: integer responses: '200': description: Order marked as completed successfully '400': $ref: '#/components/responses/BadRequest' /order/wash-certificate: post: tags: - Orders summary: Generate wash certificate description: Generate a wash certificate for an order operationId: generateWashCertificate requestBody: required: true content: application/json: schema: type: object properties: order_id: type: integer responses: '200': description: Wash certificate generated successfully '400': $ref: '#/components/responses/BadRequest' # Order Items Endpoints /order/items: get: tags: - Order Items summary: List order items description: Get all items for a specific order operationId: listOrderItems parameters: - name: order_id in: query required: true schema: type: integer responses: '200': description: Order items retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/OrderItem' post: tags: - Order Items summary: Add item to order description: Add a new item to an existing order operationId: addOrderItem requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/OrderItemCreate' responses: '201': description: Order item added successfully '400': $ref: '#/components/responses/BadRequest' put: tags: - Order Items summary: Update order item description: Update an existing order item operationId: updateOrderItem requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/OrderItemUpdate' responses: '200': description: Order item updated successfully '400': $ref: '#/components/responses/BadRequest' delete: tags: - Order Items summary: Delete order item description: Remove an item from an order operationId: deleteOrderItem parameters: - name: id in: query required: true schema: type: integer responses: '200': description: Order item deleted successfully '404': $ref: '#/components/responses/NotFound' # Departments Endpoints /departments: get: tags: - Departments summary: List departments description: Retrieve a list of all visible departments operationId: listDepartments parameters: - name: id in: query schema: type: integer description: Filter by specific department ID - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' - $ref: '#/components/parameters/SearchParam' responses: '200': description: Departments retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/Department' '401': $ref: '#/components/responses/Unauthorized' post: tags: - Departments summary: Create department description: Create a new department operationId: createDepartment requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DepartmentCreate' responses: '201': description: Department created successfully '400': $ref: '#/components/responses/BadRequest' put: tags: - Departments summary: Update department description: Update an existing department operationId: updateDepartment requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DepartmentUpdate' responses: '200': description: Department updated successfully '400': $ref: '#/components/responses/BadRequest' /departments/categories: get: tags: - Departments summary: Get department categories description: Get product categories available in a department operationId: getDepartmentCategories parameters: - name: department_id in: query required: true schema: type: integer responses: '200': description: Department categories retrieved successfully post: tags: - Departments summary: Add category to department description: Associate a product category with a department operationId: addDepartmentCategory requestBody: required: true content: application/json: schema: type: object properties: department_id: type: integer category_id: type: integer responses: '201': description: Category added to department successfully delete: tags: - Departments summary: Remove category from department operationId: removeDepartmentCategory responses: '200': {description: Success} /departments/self-serve/enabled: get: tags: - Departments summary: Get department self-serve status description: Check if self-serve is enabled for a specific department operationId: getDepartmentSelfServeEnabled parameters: - name: id in: query required: true description: Department ID schema: type: integer responses: '200': description: Successfully retrieved status content: application/json: schema: type: object properties: enabled: type: boolean '404': $ref: '#/components/responses/NotFound' put: tags: - Departments summary: Update department self-serve status description: Enable or disable self-serve for a specific department operationId: updateDepartmentSelfServeEnabled parameters: - name: id in: query required: true description: Department ID schema: type: integer - name: enabled in: query required: true description: Enabled status (true/false) schema: type: string enum: ['true', 'false'] responses: '200': description: Status updated successfully content: application/json: schema: type: object properties: message: type: string '404': $ref: '#/components/responses/NotFound' /departments/order/recommended: get: tags: - Departments summary: Get recommended order for department operationId: getDepartmentRecommendedOrder parameters: - name: department_id in: query required: true schema: {type: integer} responses: '200': {description: Success} /department/lanes: get: tags: - Departments summary: List department lanes description: Retrieve a list of all department lanes operationId: listDepartmentLanes parameters: - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' - $ref: '#/components/parameters/SearchParam' responses: '200': description: Department lanes retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/DepartmentLane' '401': $ref: '#/components/responses/Unauthorized' post: tags: - Departments summary: Create department lane description: Create a new department lane operationId: createDepartmentLane requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DepartmentLaneCreate' responses: '201': description: Department lane created successfully '400': $ref: '#/components/responses/BadRequest' put: tags: - Departments summary: Update department lane description: Update an existing department lane operationId: updateDepartmentLane requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DepartmentLaneUpdate' responses: '200': description: Department lane updated successfully '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' /guest/validation/customer-number: post: tags: - Users summary: Validate customer number description: Check if a customer number is valid and exists operationId: validateCustomerNumber security: [] requestBody: required: true content: application/json: schema: type: object required: - customer_number properties: customer_number: type: integer responses: '200': description: Customer number validation successful '400': $ref: '#/components/responses/BadRequest' /guest/departments: get: tags: - Departments summary: List public departments description: Get list of departments without authentication operationId: listGuestDepartments security: [] responses: '200': description: Departments retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/Department' /department/selfserve/questions: get: tags: - Self-Serve summary: List self-serve questions description: Retrieve a list of self-serve questions for a department, lane, or product. operationId: listSelfserveQuestions parameters: - name: id in: query description: Filter by question ID schema: type: integer - name: department in: query description: Filter by department ID schema: type: integer - name: lane in: query description: Filter by lane ID schema: type: integer - name: product in: query description: Filter by product ID schema: type: integer - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' - $ref: '#/components/parameters/SearchParam' - $ref: '#/components/parameters/FiltersParam' responses: '200': description: Successfully retrieved questions content: application/json: schema: type: array items: $ref: '#/components/schemas/DepartmentSelfserveQuestion' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' post: tags: - Self-Serve summary: Add self-serve question description: Add a new self-serve question. operationId: addSelfserveQuestion requestBody: required: true content: application/json: schema: type: object required: - department - lane - product - question - description properties: department: type: integer lane: type: integer product: type: integer question: type: string description: type: string condition_id: type: integer nullable: true order_priority: type: integer default: 0 responses: '200': description: Successfully added question content: application/json: schema: $ref: '#/components/schemas/DepartmentSelfserveQuestion' '400': $ref: '#/components/responses/BadRequest' '500': $ref: '#/components/responses/InternalServerError' put: tags: - Self-Serve summary: Update self-serve question description: Update an existing self-serve question. operationId: updateSelfserveQuestion parameters: - name: id in: query required: true description: Question ID schema: type: integer requestBody: content: application/json: schema: type: object properties: department: type: integer lane: type: integer product: type: integer question: type: string description: type: string condition_id: type: integer nullable: true order_priority: type: integer responses: '200': description: Successfully updated question content: application/json: schema: $ref: '#/components/schemas/DepartmentSelfserveQuestion' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' delete: tags: - Self-Serve summary: Delete self-serve question description: Delete a self-serve question by ID. operationId: deleteSelfserveQuestion parameters: - name: id in: query required: true description: Question ID schema: type: integer responses: '200': description: Successfully deleted question content: application/json: schema: type: string example: Question deleted '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' /department/selfserve/conditions: get: tags: - Self-Serve summary: List self-serve conditions description: Retrieve a list of self-serve conditions for a department, lane, or product. operationId: listSelfserveConditions parameters: - name: id in: query description: Filter by condition ID schema: type: integer - name: department in: query description: Filter by department ID schema: type: integer - name: lane in: query description: Filter by lane ID schema: type: integer - name: product in: query description: Filter by product ID schema: type: integer - name: condition_id in: query description: Filter by condition ID schema: type: integer - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' - $ref: '#/components/parameters/SearchParam' - $ref: '#/components/parameters/FiltersParam' responses: '200': description: Successfully retrieved conditions content: application/json: schema: type: array items: $ref: '#/components/schemas/DepartmentSelfserveCondition' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' post: tags: - Self-Serve summary: Add self-serve condition description: Add a new self-serve condition. operationId: addSelfserveCondition requestBody: required: true content: application/json: schema: type: object required: - department - lane - product - name - description properties: department: type: integer lane: type: integer product: type: integer condition_id: type: integer nullable: true name: type: string description: type: string responses: '200': description: Successfully added condition content: application/json: schema: $ref: '#/components/schemas/DepartmentSelfserveCondition' '400': $ref: '#/components/responses/BadRequest' '500': $ref: '#/components/responses/InternalServerError' put: tags: - Self-Serve summary: Update self-serve condition description: Update an existing self-serve condition. operationId: updateSelfserveCondition parameters: - name: id in: query required: true description: Condition ID schema: type: integer requestBody: content: application/json: schema: type: object properties: department: type: integer lane: type: integer product: type: integer condition_id: type: integer nullable: true name: type: string description: type: string responses: '200': description: Successfully updated condition content: application/json: schema: $ref: '#/components/schemas/DepartmentSelfserveCondition' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' delete: tags: - Self-Serve summary: Delete self-serve condition description: Delete a self-serve condition. operationId: deleteSelfserveCondition parameters: - name: id in: query required: true description: Condition ID schema: type: integer responses: '200': description: Successfully deleted condition content: application/json: schema: type: string example: Condition deleted '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' /department/selfserve/condition/rules: get: tags: - Self-Serve summary: List self-serve condition rules description: Retrieve a list of self-serve condition rules. operationId: listSelfserveConditionRules parameters: - name: id in: query description: Filter by rule ID schema: type: integer - name: condition_id in: query description: Filter by condition ID schema: type: integer - name: type in: query description: Filter by rule type schema: type: string - name: object_type in: query description: Filter by object type schema: type: string - name: object_id in: query description: Filter by object ID schema: type: integer - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' - $ref: '#/components/parameters/SearchParam' - $ref: '#/components/parameters/FiltersParam' responses: '200': description: Successfully retrieved condition rules content: application/json: schema: type: array items: $ref: '#/components/schemas/DepartmentSelfserveConditionRule' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' post: tags: - Self-Serve summary: Add self-serve condition rule description: Add a new self-serve condition rule. operationId: addSelfserveConditionRule requestBody: required: true content: application/json: schema: type: object required: - condition_id - type - object_type - object_id - name - description properties: condition_id: type: integer type: type: string object_type: type: string object_id: type: integer name: type: string description: type: string responses: '200': description: Successfully added condition rule content: application/json: schema: $ref: '#/components/schemas/DepartmentSelfserveConditionRule' '400': $ref: '#/components/responses/BadRequest' '500': $ref: '#/components/responses/InternalServerError' put: tags: - Self-Serve summary: Update self-serve condition rule description: Update an existing self-serve condition rule. operationId: updateSelfserveConditionRule parameters: - name: id in: query required: true description: Rule ID schema: type: integer requestBody: content: application/json: schema: type: object properties: condition_id: type: integer type: type: string object_type: type: string object_id: type: integer name: type: string description: type: string responses: '200': description: Successfully updated condition rule content: application/json: schema: $ref: '#/components/schemas/DepartmentSelfserveConditionRule' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' delete: tags: - Self-Serve summary: Delete self-serve condition rule description: Delete a self-serve condition rule. operationId: deleteSelfserveConditionRule parameters: - name: id in: query required: true description: Rule ID schema: type: integer responses: '200': description: Successfully deleted condition rule content: application/json: schema: type: string example: Rule deleted '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' /department/selfserve/vehicle/conditions: get: tags: - Self-Serve summary: List vehicle conditions description: Retrieve a list of vehicle conditions for a department, lane, reg, or question. Customers will only see their own vehicle conditions. operationId: listSelfserveVehicleConditions parameters: - name: id in: query description: Filter by condition ID schema: type: integer - name: department in: query description: Filter by department ID schema: type: integer - name: lane in: query description: Filter by lane ID schema: type: integer - name: reg in: query description: Filter by vehicle registration number schema: type: string - name: question in: query description: Filter by question ID schema: type: integer - name: customer_id in: query description: Filter by customer ID schema: type: integer - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' - $ref: '#/components/parameters/SearchParam' - $ref: '#/components/parameters/FiltersParam' responses: '200': description: Successfully retrieved vehicle conditions content: application/json: schema: type: array items: $ref: '#/components/schemas/DepartmentSelfserveVehicleCondition' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' post: tags: - Self-Serve summary: Add vehicle condition description: Add a new vehicle condition (answer to a question). Customers can only add conditions for their own vehicles. operationId: addSelfserveVehicleCondition requestBody: required: true content: application/json: schema: type: object required: - department - lane - reg - question properties: department: type: integer lane: type: integer reg: type: string question: type: integer value: type: boolean customer_id: type: integer nullable: true responses: '200': description: Successfully added vehicle condition content: application/json: schema: $ref: '#/components/schemas/DepartmentSelfserveVehicleCondition' '400': $ref: '#/components/responses/BadRequest' '500': $ref: '#/components/responses/InternalServerError' put: tags: - Self-Serve summary: Update vehicle condition description: Update an existing vehicle condition. Customers can only update conditions for their own vehicles. operationId: updateSelfserveVehicleCondition parameters: - name: id in: query required: true description: Condition ID schema: type: integer requestBody: content: application/json: schema: type: object properties: department: type: integer lane: type: integer reg: type: string question: type: integer value: type: boolean customer_id: type: integer nullable: true responses: '200': description: Successfully updated vehicle condition content: application/json: schema: $ref: '#/components/schemas/DepartmentSelfserveVehicleCondition' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' delete: tags: - Self-Serve summary: Delete vehicle condition description: Delete a vehicle condition. Customers can only delete conditions for their own vehicles. operationId: deleteSelfserveVehicleCondition parameters: - name: id in: query required: true description: Condition ID schema: type: integer responses: '200': description: Successfully deleted vehicle condition content: application/json: schema: type: string example: Condition deleted '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' /department/selfserve/tasks: get: tags: - Self-Serve summary: List self-serve tasks description: Retrieve a list of self-serve tasks for a department, lane, product, or condition_id. operationId: listSelfserveTasks parameters: - name: id in: query description: Filter by task ID schema: type: integer - name: department in: query description: Filter by department ID schema: type: integer - name: lane in: query description: Filter by lane ID schema: type: integer - name: product in: query description: Filter by product ID schema: type: integer - name: condition_id in: query description: Filter by condition ID schema: type: integer - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' - $ref: '#/components/parameters/SearchParam' - $ref: '#/components/parameters/FiltersParam' responses: '200': description: Successfully retrieved tasks content: application/json: schema: type: array items: $ref: '#/components/schemas/DepartmentSelfserveTask' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' post: tags: - Self-Serve summary: Add self-serve task description: Add a new self-serve task. operationId: addSelfserveTask requestBody: required: true content: application/json: schema: type: object required: - department - lane - product - task - description properties: department: type: integer lane: type: integer product: type: integer condition_id: type: integer nullable: true task: type: string description: type: string order_priority: type: integer default: 0 responses: '200': description: Successfully added task content: application/json: schema: $ref: '#/components/schemas/DepartmentSelfserveTask' '400': $ref: '#/components/responses/BadRequest' '500': $ref: '#/components/responses/InternalServerError' put: tags: - Self-Serve summary: Update self-serve task description: Update an existing self-serve task. operationId: updateSelfserveTask parameters: - name: id in: query required: true description: Task ID schema: type: integer requestBody: content: application/json: schema: type: object properties: department: type: integer lane: type: integer product: type: integer condition_id: type: integer nullable: true task: type: string description: type: string order_priority: type: integer responses: '200': description: Successfully updated task content: application/json: schema: $ref: '#/components/schemas/DepartmentSelfserveTask' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' delete: tags: - Self-Serve summary: Delete self-serve task description: Delete a self-serve task by ID. operationId: deleteSelfserveTask parameters: - name: id in: query required: true description: Task ID schema: type: integer responses: '200': description: Successfully deleted task content: application/json: schema: type: string example: Task deleted '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' /department/selfserve/tasks/attachments: get: tags: - Self-Serve summary: List task attachments description: Retrieve a list of attachments for a specific self-serve task. operationId: listSelfserveTaskAttachments parameters: - name: id in: query required: true description: Task ID schema: type: integer responses: '200': description: Successfully retrieved task attachments '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' delete: tags: - Self-Serve summary: Delete task attachment description: Remove an attachment from a specific self-serve task. operationId: deleteSelfserveTaskAttachment parameters: - name: task_id in: query required: true description: Task ID schema: type: integer - name: attachment_id in: query required: true description: Attachment ID schema: type: integer responses: '200': description: Attachment deleted successfully content: application/json: schema: type: object properties: message: type: string example: Attachment deleted successfully '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' /department/selfserve/tasks/attachments/upload: post: tags: - Self-Serve summary: Upload task attachment description: Upload a new attachment to a specific self-serve task using base64 encoding. operationId: uploadSelfserveTaskAttachment requestBody: required: true content: application/json: schema: type: object required: - task_id - base64_file - file_name properties: task_id: type: integer base64_file: type: string description: Base64 encoded file content file_name: type: string description: Name of the file including extension responses: '200': description: Attachment uploaded successfully '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /department/selfserve/tasks/attachments/download: get: tags: - Self-Serve summary: Download task attachment description: Generate a download link for a specific self-serve task attachment. operationId: downloadSelfserveTaskAttachment parameters: - name: task_id in: query required: true description: Task ID schema: type: integer - name: attachment_id in: query required: true description: Attachment ID schema: type: integer responses: '200': description: Successfully generated download link content: application/json: schema: type: object properties: download_link: type: string format: uri '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' # Products Endpoints /products: get: tags: - Products summary: List products description: Retrieve a list of products with optional filters for customer pricing and department operationId: listProducts parameters: - name: customer_id in: query schema: type: integer description: Customer ID for custom pricing - name: department_id in: query schema: type: integer description: Department ID for department-specific pricing - name: category in: query schema: type: integer description: Filter by category ID - name: id in: query schema: type: integer description: Get specific product by ID - name: final_price in: query schema: type: boolean description: Whether to return final prices including discounts - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' responses: '200': description: Products retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/Product' post: tags: - Products summary: Create product description: Create a new product operationId: createProduct requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ProductCreate' responses: '201': description: Product created successfully '400': $ref: '#/components/responses/BadRequest' put: tags: - Products summary: Update product description: Update an existing product operationId: updateProduct requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ProductUpdate' responses: '200': description: Product updated successfully '400': $ref: '#/components/responses/BadRequest' # Categories Endpoints /categories: get: tags: - Categories summary: List categories description: Retrieve a list of product categories operationId: listCategories parameters: - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' responses: '200': description: Categories retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/Category' post: tags: - Categories summary: Create category description: Create a new product category operationId: createCategory requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CategoryCreate' responses: '201': description: Category created successfully put: tags: - Categories summary: Update category description: Update an existing category operationId: updateCategory requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CategoryUpdate' responses: '200': description: Category updated successfully # Bookings Endpoints /bookings: get: tags: - Bookings summary: List bookings description: Retrieve a list of bookings operationId: listBookings parameters: - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' responses: '200': description: Bookings retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/Booking' put: tags: - Bookings summary: Update booking description: Update an existing booking operationId: updateBooking requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BookingUpdate' responses: '200': description: Booking updated successfully /user/bookings: get: tags: - Bookings summary: Get user bookings description: Retrieve bookings for the authenticated user operationId: getUserBookings responses: '200': description: User bookings retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/Booking' /order-bookings: get: tags: - Bookings summary: List order bookings operationId: listOrderBookings parameters: - name: id in: query required: false schema: {type: integer} responses: '200': description: Success # Goals Endpoints /goals/department: get: tags: - Goals summary: List or get department goals description: | Retrieve a list of department goals or a single goal when `id` is provided. Access control: - A user may only access goals where the goal's `departments` set is a subset of the user's departments. - Users with the `superuser` permission may access all goals. operationId: listDepartmentGoals parameters: - name: id in: query required: false schema: { type: integer } description: When provided, returns the single goal with this id (if accessible) - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' - $ref: '#/components/parameters/SearchParam' - $ref: '#/components/parameters/FiltersParam' responses: '200': description: Goals retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/DepartmentGoal' '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '404': { $ref: '#/components/responses/NotFound' } post: tags: - Goals summary: Create department goal description: | Create a new department goal. Access control: - The provided `departments` must be a subset of the user's departments unless the user has `superuser`. operationId: createDepartmentGoal requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DepartmentGoalCreate' responses: '201': description: Department goal created successfully content: application/json: schema: $ref: '#/components/schemas/DepartmentGoal' '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } put: tags: - Goals summary: Update department goal description: | Update an existing department goal by `id`. Access control: - The creator (`created_by`) may update regardless of department membership. - Otherwise the user must satisfy the same subset rule as for read access, and any new `departments` provided must also be a subset unless the user has `superuser`. operationId: updateDepartmentGoal requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DepartmentGoalUpdate' responses: '200': description: Department goal updated successfully content: application/json: schema: $ref: '#/components/schemas/DepartmentGoal' '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '404': { $ref: '#/components/responses/NotFound' } delete: tags: - Goals summary: Delete department goal description: | Delete a department goal by `id`. Access control: - The creator (`created_by`) may delete regardless of department membership. - Otherwise the user must satisfy the subset rule or have `superuser`. operationId: deleteDepartmentGoal parameters: - name: id in: query required: true schema: { type: integer } description: ID of the goal to delete responses: '200': description: Department goal deleted successfully '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '404': { $ref: '#/components/responses/NotFound' } '/order-bookings': post: tags: - Bookings summary: Create order booking operationId: createOrderBooking requestBody: required: true content: application/json: schema: type: object required: [department, reg_1, datetime, items] properties: customer_number: {type: integer} department: {type: integer} reg_1: {type: string} reg_2: {type: string} reg_3: {type: string} datetime: {type: string, format: date-time} note: {type: string} reference: {type: string} po: {type: string} pickup: {type: boolean} items: type: array items: type: object required: [id, quantity] properties: id: {type: integer} quantity: {type: integer} responses: '200': description: Success put: tags: - Bookings summary: Update order booking operationId: updateOrderBooking requestBody: required: true content: application/json: schema: type: object required: [id] properties: id: {type: integer} customer_number: {type: integer} department: {type: integer} reg_1: {type: string} reg_2: {type: string} reg_3: {type: string} datetime: {type: string, format: date-time} note: {type: string} reference: {type: string} po: {type: string} pickup: {type: boolean} order_id: {type: integer} items: type: array items: type: object required: [id, quantity] properties: id: {type: integer} quantity: {type: integer} responses: '200': description: Success delete: tags: - Bookings summary: Delete order booking operationId: deleteOrderBooking parameters: - name: id in: query required: true schema: {type: integer} responses: '200': description: Success /order-bookings/complete: post: tags: - Bookings summary: Complete order booking operationId: completeOrderBooking requestBody: required: true content: application/json: schema: type: object required: [id] properties: id: {type: integer} safety_seal: {type: integer} responses: '200': description: Success /admin/bookings/sync: post: tags: - Bookings summary: Sync booking from external system operationId: syncBooking responses: '200': description: Success /admin/bookings/department/count: get: tags: - Bookings summary: Get department unfulfilled bookings count operationId: getDepartmentBookingCount parameters: - name: department_id in: query required: true schema: {type: integer} responses: '200': description: Success /user/bookings/washcertificate/download: post: tags: - Bookings summary: Get download link for own wash certificate operationId: downloadOwnWashCertificate requestBody: required: true content: application/json: schema: type: object required: [id] properties: id: {type: integer} responses: '200': description: Success /bookings/download_pdf: get: tags: - Bookings summary: Download booking PDF operationId: downloadBookingPdf parameters: - name: id in: query required: true schema: {type: integer} responses: '200': description: Success /admin/bookings/delete: post: tags: - Bookings summary: Delete booking (admin) operationId: adminDeleteBooking requestBody: required: true content: application/json: schema: type: object required: [id] properties: id: {type: integer} responses: '200': description: Success /superuser/bookings/sync/all: post: tags: - Bookings summary: Sync all bookings from external system operationId: syncAllBookings responses: '200': description: Success /admin/bookings/completeWashWithoutWashCertificate: post: tags: - Bookings summary: Complete wash without wash certificate operationId: completeWashWithoutWashCertificate requestBody: required: true content: application/json: schema: type: object required: [id] properties: id: {type: integer} responses: '200': description: Success /user/bookings/delete: post: tags: - Bookings summary: Delete own booking operationId: deleteOwnBooking requestBody: required: true content: application/json: schema: type: object required: [id] properties: id: {type: integer} responses: '200': description: Success # Invoices Endpoints /invoices/draft: get: tags: - Invoices summary: List draft invoices description: Retrieve a list of draft invoices operationId: listDraftInvoices parameters: - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' responses: '200': description: Draft invoices retrieved successfully /invoices/draft/close: post: tags: - Invoices summary: Close draft invoice description: Close a draft invoice operationId: closeDraftInvoice requestBody: required: true content: application/json: schema: type: object properties: id: type: integer responses: '200': description: Draft invoice closed successfully /invoices/pdf: get: tags: - Invoices summary: Get invoice PDF description: Download an invoice as PDF operationId: getInvoicePdf parameters: - name: id in: query required: true schema: type: integer responses: '200': description: PDF retrieved successfully content: application/pdf: schema: type: string format: binary /user/invoices: get: tags: - Invoices summary: Get user invoices description: Retrieve invoices for the authenticated user operationId: getUserInvoices responses: '200': description: User invoices retrieved successfully /collected-invoices: get: tags: - Invoices summary: List collected invoices description: Get list of collected invoices operationId: listCollectedInvoices parameters: - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' responses: '200': description: Collected invoices retrieved successfully post: tags: - Invoices summary: Create collected invoice description: Create a new collected invoice operationId: createCollectedInvoice responses: '201': description: Collected invoice created successfully put: tags: - Invoices summary: Update collected invoice description: Update a collected invoice operationId: updateCollectedInvoice responses: '200': description: Collected invoice updated successfully /collected-invoices/ready-to-invoice: get: tags: - Invoices summary: Get invoices ready to process description: Get collected invoices that are ready to be processed operationId: getReadyToInvoice responses: '200': description: Ready invoices retrieved successfully /superuser/invoicing/period: get: tags: - Invoices summary: Get invoicing periods description: Retrieve invoicing periods for superusers operationId: getInvoicingPeriods parameters: - name: dateFrom in: query required: true schema: {type: string, format: date} - name: dateTo in: query required: true schema: {type: string, format: date} responses: '200': description: Invoicing periods retrieved successfully /superuser/invoicing/period/distribution/fixed-pricing: get: tags: - Invoices summary: Get fixed pricing distribution description: Get invoicing distribution for fixed pricing items operationId: getInvoicingFixedPricingDistribution parameters: - name: dateFrom in: query required: true schema: {type: string, format: date} - name: dateTo in: query required: true schema: {type: string, format: date} responses: '200': description: Fixed pricing distribution retrieved successfully /superuser/invoicing/period/distribution/wash-subscriptions: get: tags: - Invoices summary: Get wash subscriptions distribution description: Get invoicing distribution for wash subscriptions operationId: getInvoicingWashSubscriptionsDistribution parameters: - name: dateFrom in: query required: true schema: {type: string, format: date} - name: dateTo in: query required: true schema: {type: string, format: date} responses: '200': description: Wash subscriptions distribution retrieved successfully # Vehicles Endpoints /vehicles: get: tags: - Vehicles summary: List vehicles description: Get list of vehicles operationId: listVehicles parameters: - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' - name: id in: query schema: {type: integer} - name: reg in: query schema: {type: string} - name: customer_id in: query schema: {type: integer} responses: '200': description: Vehicles retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/Vehicle' post: tags: - Vehicles summary: Add vehicle operationId: addVehicle requestBody: required: true content: application/json: schema: type: object required: [reg, customer_id] properties: reg: {type: string} customer_id: {type: integer} type: {type: integer} reference: {type: string} responses: '200': {description: Success} put: tags: - Vehicles summary: Edit vehicle operationId: editVehicle requestBody: required: true content: application/json: schema: type: object required: [id] properties: id: {type: integer} reg: {type: string} customer_id: {type: integer} type: {type: integer} reference: {type: string} responses: '200': {description: Success} delete: tags: - Vehicles summary: Delete vehicle operationId: deleteVehicle parameters: - name: id in: query required: true schema: {type: integer} responses: '200': {description: Success} /vehicles/addons/available: get: tags: - Vehicles summary: Get available vehicle addons description: Get list of available addons for a vehicle operationId: getAvailableVehicleAddons parameters: - name: vehicle_id in: query schema: type: integer responses: '200': description: Available addons retrieved successfully /vehicles/addons/toggle: post: tags: - Vehicles summary: Toggle vehicle addon description: Enable or disable a vehicle addon operationId: toggleVehicleAddon requestBody: required: true content: application/json: schema: type: object properties: vehicle_id: type: integer addon_id: type: integer enabled: type: boolean responses: '200': description: Vehicle addon toggled successfully /department/vehicles/unknown-customer: get: tags: - Vehicles summary: Get unknown customer vehicles in department operationId: getUnknownCustomerVehicles responses: '200': {description: Success} /department/vehicle/customer-suggestions: get: tags: - Vehicles summary: Get vehicle customer suggestions operationId: getVehicleCustomerSuggestions parameters: - name: reg in: query required: true schema: {type: string} responses: '200': {description: Success} /vehicles/set-auto-start-on-lpr: post: tags: - Vehicles summary: Set auto start on LPR operationId: setVehicleAutoStartOnLpr requestBody: required: true content: application/json: schema: type: object required: [reg, auto_start_on_lpr] properties: reg: {type: string} auto_start_on_lpr: {type: boolean} responses: '200': {description: Success} /vehicles/set-vehicle-type-id: post: tags: - Vehicles summary: Set vehicle type ID operationId: setVehicleTypeId requestBody: required: true content: application/json: schema: type: object required: [reg, vehicle_type_id] properties: reg: {type: string} vehicle_type_id: {type: integer} responses: '200': {description: Success} /superuser/users-with-vehicle-subscriptions: get: tags: - Vehicles summary: Get users with vehicle subscriptions operationId: getUsersWithVehicleSubscriptions responses: '200': {description: Success} /vehicles/status: get: tags: - Vehicles summary: Get vehicle status operationId: getVehicleStatus parameters: - name: reg in: query required: true schema: {type: string} responses: '200': {description: Success} /vehicles/search: get: tags: - Vehicles summary: Search vehicles operationId: searchVehicles parameters: - name: search in: query required: true schema: {type: string} responses: '200': {description: Success} # Notifications Endpoints /notifications: get: tags: - Notifications summary: List notifications description: Get list of notifications operationId: listNotifications parameters: - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' responses: '200': description: Notifications retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/Notification' post: tags: - Notifications summary: Create notification description: Create a new notification operationId: createNotification requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/NotificationCreate' responses: '201': description: Notification created successfully delete: tags: - Notifications summary: Delete notification description: Delete a notification operationId: deleteNotification parameters: - name: id in: query required: true schema: type: integer responses: '200': description: Notification deleted successfully # Statistics Endpoints /statistics/bookings/new: get: tags: - Statistics summary: Get new bookings statistics description: Get statistics for new bookings operationId: getNewBookingsStats responses: '200': description: New bookings statistics retrieved successfully /orders/module/stripe/payment_intent: get: tags: - Orders summary: Get Stripe payment intent operationId: getStripePaymentIntent parameters: - name: id in: query required: true schema: {type: integer} responses: '200': description: Success post: tags: - Orders summary: Create Stripe payment intent operationId: createStripePaymentIntent requestBody: required: true content: application/json: schema: type: object required: [id, reader] properties: id: {type: integer} reader: {type: string} tax_percentage: {type: integer} responses: '200': description: Success delete: tags: - Orders summary: Delete Stripe payment intent operationId: deleteStripePaymentIntent parameters: - name: id in: query required: true schema: {type: integer} responses: '200': description: Success /orders/module/stripe/payment_intent/capture: post: tags: - Orders summary: Capture Stripe payment intent operationId: captureStripePaymentIntent requestBody: required: true content: application/json: schema: type: object required: [id] properties: id: {type: integer} responses: '200': description: Success /orders/module/stripe/debug/simulate_payment: post: tags: - Orders summary: Simulate Stripe payment operationId: simulateStripePayment requestBody: required: true content: application/json: schema: type: object required: [id] properties: id: {type: integer} responses: '200': description: Success /statistics/orders/new: get: tags: - Statistics summary: Get new orders statistics description: Get statistics for new orders operationId: getNewOrdersStats responses: '200': description: New orders statistics retrieved successfully /statistics/income/today: get: tags: - Statistics summary: Get today's income description: Get income statistics for today operationId: getTodayIncome responses: '200': description: Today's income statistics retrieved successfully /statistics/income/yesterday: get: tags: - Statistics summary: Get yesterday's income description: Get income statistics for yesterday operationId: getYesterdayIncome responses: '200': description: Yesterday's income statistics retrieved successfully /statistics/income/this-month: get: tags: - Statistics summary: Get this month's income description: Get income statistics for the current month operationId: getThisMonthIncome responses: '200': description: This month's income statistics retrieved successfully /statistics/income/last-month: get: tags: - Statistics summary: Get last month's income description: Get income statistics for the previous month operationId: getLastMonthIncome responses: '200': description: Last month's income statistics retrieved successfully /statistics/income/this-year: get: tags: - Statistics summary: Get this year's income description: Get income statistics for the current year operationId: getThisYearIncome responses: '200': description: This year's income statistics retrieved successfully /statistics/income/departments: get: tags: - Statistics summary: Get total income today by departments operationId: getTotalIncomeTodayByDepartments responses: '200': {description: Success} /statistics/economic/totals: get: tags: - Statistics summary: Get total economic statistics operationId: getEconomicTotals responses: '200': {description: Success} /statistics/economic/totals/department_sent_invoice_totals: get: tags: - Statistics summary: Get department sent invoice totals operationId: getDepartmentSentInvoiceTotals responses: '200': {description: Success} /statistics/economic/totals/department_draft_invoice_totals: get: tags: - Statistics summary: Get department draft invoice totals operationId: getDepartmentDraftInvoiceTotals responses: '200': {description: Success} # Worker Endpoints /worker/version: get: tags: - Worker summary: Get worker version description: Get the current version of the system worker operationId: getWorkerVersion responses: '200': description: Worker version retrieved successfully /worker/update-version: get: tags: - Worker summary: Update worker version description: Set the target version for the worker update operationId: updateWorkerVersion parameters: - name: version in: query required: true schema: type: string responses: '200': description: Version update target set successfully /worker/status: get: tags: - Worker summary: Get worker status description: Get detailed status of the system worker operationId: getWorkerStatus responses: '200': description: Worker status retrieved successfully /worker/debug: get: tags: - Worker summary: Debug worker description: Execute debug commands on the worker (often restricted) operationId: debugWorker responses: '200': description: Debug information retrieved successfully '403': $ref: '#/components/responses/Forbidden' /worker/debug/on: get: tags: - Worker summary: Enable worker debug operationId: enableWorkerDebug responses: '200': description: Worker debug enabled '403': $ref: '#/components/responses/Forbidden' /worker/debug/off: get: tags: - Worker summary: Disable worker debug operationId: disableWorkerDebug responses: '200': description: Worker debug disabled '403': $ref: '#/components/responses/Forbidden' /worker/licenseplates: get: tags: - Worker summary: Get unique license plates description: Fetch all unique license plates from various database tables operationId: getWorkerLicensePlates responses: '200': description: License plates retrieved successfully '403': $ref: '#/components/responses/Forbidden' /economic/doesCustomerExist: get: tags: - Modules summary: Check if customer exists in e-conomic operationId: checkEconomicCustomerExists parameters: - name: cvr in: query required: true schema: type: string responses: '200': description: Customer check completed '404': $ref: '#/components/responses/NotFound' /cvr/lookup: get: tags: - Modules summary: Lookup CVR information description: Get detailed information for a CVR number operationId: lookupCvr parameters: - name: cvr in: query required: true schema: type: string responses: '200': description: CVR information retrieved successfully /cvr/search: get: tags: - Modules summary: Search CVR description: Search for companies by name or CVR operationId: searchCvr parameters: - name: query in: query required: true schema: type: string minLength: 2 responses: '200': description: Search results retrieved successfully # Plate Scans Endpoints /numberplatescans: get: tags: - Plate Scans summary: List plate scans description: Get a list of license plate scans operationId: listPlateScans parameters: - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' - $ref: '#/components/parameters/SearchParam' responses: '200': description: Plate scans retrieved successfully post: tags: - Plate Scans summary: Record plate scan description: Record a new license plate scan operationId: recordPlateScan requestBody: required: true content: application/json: schema: type: object required: - plate - lane_id properties: plate: type: string lane_id: type: integer responses: '201': description: Plate scan recorded successfully /numberplatescans/department: post: tags: - Plate Scans summary: Record plate scan for department operationId: recordDepartmentPlateScan requestBody: required: true content: application/json: schema: type: object required: - plate - department_id properties: plate: type: string department_id: type: integer responses: '201': description: Plate scan recorded successfully /numberplatescans/post: get: tags: - Plate Scans summary: Get post-scan results operationId: getPlateScanPostResults responses: '200': description: Post-scan results retrieved successfully /numberplatescanners: get: tags: - Plate Scans summary: List plate scanners description: Get a list of all number plate scanners operationId: listPlateScanners parameters: - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' - $ref: '#/components/parameters/SearchParam' responses: '200': description: Plate scanners retrieved successfully post: tags: - Plate Scans summary: Add plate scanner operationId: addPlateScanner requestBody: required: true content: application/json: schema: type: object required: [department_id, name, notes] properties: department_id: {type: integer} name: {type: string} notes: {type: string} responses: '201': description: Plate scanner added successfully put: tags: - Plate Scans summary: Update plate scanner operationId: updatePlateScanner requestBody: required: true content: application/json: schema: type: object required: [id, department_id, name, notes] properties: id: {type: integer} department_id: {type: integer} name: {type: string} notes: {type: string} responses: '200': description: Plate scanner updated successfully /department/numberplatescanners: get: tags: - Plate Scans summary: List department plate scanners operationId: listDepartmentPlateScanners parameters: - name: id in: query required: true schema: type: integer responses: '200': description: Department plate scanners retrieved successfully /relay/button/press/post: get: tags: - Plate Scans summary: Add button press operationId: addButtonPress parameters: - name: token in: query required: true schema: {type: string} responses: '201': description: Button press recorded # Module - e-conomic Endpoints /economic/customers/import: post: tags: - Modules summary: Import e-conomic customers description: Import customers from e-conomic operationId: importEconomicCustomers responses: '200': description: Customers imported successfully /economic/departments: get: tags: - Modules summary: Get e-conomic departments operationId: getEconomicDepartments responses: '200': description: Success /economic/products: get: tags: - Modules summary: Get e-conomic products operationId: getEconomicProducts responses: '200': description: Success /modules/economic/customer: get: tags: - Modules summary: Get e-conomic customer details operationId: getEconomicCustomer parameters: - name: customer_number in: query required: true schema: {type: integer} responses: '200': description: Success post: tags: - Modules summary: Create e-conomic customer operationId: createEconomicCustomer requestBody: required: true content: application/json: schema: type: object required: [customer_number, cvr, email, phone, name] properties: customer_number: {type: integer} cvr: {type: integer} email: {type: string} phone: {type: integer} name: {type: string} responses: '200': description: Success /economic/layouts: get: tags: - Modules summary: Get e-conomic layouts description: Get available invoice layouts from e-conomic operationId: getEconomicLayouts responses: '200': description: Layouts retrieved successfully /economic/payment-terms: get: tags: - Modules summary: Get e-conomic payment terms description: Get available payment terms from e-conomic operationId: getEconomicPaymentTerms responses: '200': description: Payment terms retrieved successfully /economic/invoice/draft/export: post: tags: - Modules summary: Export draft invoice to e-conomic description: Export a draft invoice to e-conomic operationId: exportDraftInvoiceToEconomic responses: '200': description: Draft invoice exported successfully /economic/invoice/export: post: tags: - Modules summary: Export invoice to e-conomic description: Export a booked invoice to e-conomic operationId: exportInvoiceToEconomic responses: '200': description: Invoice exported successfully # Module - Stripe Endpoints /modules/stripe/customers: get: tags: - Modules summary: List Stripe customers description: Get list of Stripe customers operationId: listStripeCustomers responses: '200': description: Stripe customers retrieved successfully /modules/stripe/products: get: tags: - Modules summary: List Stripe products description: Get list of Stripe products operationId: listStripeProducts responses: '200': description: Stripe products retrieved successfully /modules/stripe/prices: get: tags: - Modules summary: List Stripe prices description: Get list of Stripe prices operationId: listStripePrices responses: '200': description: Stripe prices retrieved successfully /modules/stripe/invoice: post: tags: - Modules summary: Create Stripe invoice description: Create an invoice in Stripe operationId: createStripeInvoice responses: '201': description: Stripe invoice created successfully /modules/stripe/terminal/readers: get: tags: - Modules summary: List Stripe terminal readers operationId: listStripeTerminalReaders responses: '200': description: Success /modules/stripe/terminal/locations: get: tags: - Modules summary: List Stripe terminal locations operationId: listStripeTerminalLocations responses: '200': description: Success /modules/stripe/department/terminal/location: get: tags: - Modules summary: Get department terminal location operationId: getDepartmentTerminalLocation parameters: - name: id in: query required: true schema: {type: integer} responses: '200': description: Success post: tags: - Modules summary: Set department terminal location operationId: setDepartmentTerminalLocation requestBody: required: true content: application/json: schema: type: object required: [id, location] properties: id: {type: integer} location: {type: string} responses: '200': description: Success /modules/stripe/department/terminal/readers: get: tags: - Modules summary: Get department terminal readers operationId: getDepartmentTerminalReaders parameters: - name: id in: query required: true schema: {type: integer} responses: '200': description: Success # Module - Backup Endpoints /modules/backup/backups: get: tags: - Modules summary: List backup modules operationId: listBackupModules responses: '200': description: Success post: tags: - Modules summary: Create backup module operationId: createBackupModule requestBody: required: true content: application/json: schema: type: object required: [name, description] properties: name: {type: string} description: {type: string} responses: '200': description: Success # Module - XLVask Endpoints /modules/xlvask/usageLog: get: tags: - Modules summary: Get XLVask usage logs description: Retrieve usage logs from XLVask system operationId: getXlvaskUsageLogs parameters: - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' responses: '200': description: Usage logs retrieved successfully /modules/xlvask/vehicles: get: tags: - Modules summary: List XLVask vehicles description: Get list of vehicles from XLVask operationId: listXlvaskVehicles responses: '200': description: XLVask vehicles retrieved successfully /modules/xlvask/customers: get: tags: - Modules summary: List XLVask customers description: Get list of customers from XLVask operationId: listXlvaskCustomers responses: '200': description: XLVask customers retrieved successfully /modules/action-logs: get: tags: - Modules summary: List module action logs description: Retrieve a paginated list of module action logs with searching and filtering operationId: listModuleActionLogs parameters: - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/LimitParam' - $ref: '#/components/parameters/SearchParam' - $ref: '#/components/parameters/FiltersParam' responses: '200': description: Module action logs retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/ModuleActionLog' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' # Module - Self-Serve Endpoints /modules/self-serve/lane/status: get: tags: - Modules summary: Get self-serve lane status description: Retrieve the current status of a self-serve lane operationId: getSelfServeLaneStatus parameters: - name: lane_id in: query required: true schema: type: integer responses: '200': description: Lane status retrieved successfully content: application/json: schema: $ref: '#/components/schemas/SelfServeLaneStatus' /modules/self-serve/lane/command: post: tags: - Modules summary: Send self-serve lane command description: Send a command (e.g., start, stop, reset) to a self-serve lane operationId: sendSelfServeLaneCommand requestBody: required: true content: application/json: schema: type: object required: - lane_id - command properties: lane_id: type: integer command: type: string enum: [START, STOP, RESET, RESERVE, RELEASE] license_plate: type: string description: Required for START command responses: '200': description: Command sent successfully content: application/json: schema: $ref: '#/components/schemas/SelfServeLaneStatus' # Module - Other Integration Endpoints /modules/motorapi/lookup: get: tags: - Modules summary: Lookup vehicle via MotorAPI description: Look up vehicle information using license plate operationId: motorApiLookup parameters: - name: plate in: query required: true schema: type: string responses: '200': description: Vehicle information retrieved successfully /modules/virkdata/search: get: tags: - Modules summary: Search VirkData description: Search for company information in VirkData operationId: virkdataSearch parameters: - name: search in: query required: true schema: type: string responses: '200': description: Company information retrieved successfully /modules/fxratesapi/rate: get: tags: - Modules summary: Get exchange rate description: Get current exchange rate operationId: getExchangeRate parameters: - name: from in: query required: true schema: type: string - name: to in: query required: true schema: type: string responses: '200': description: Exchange rate retrieved successfully /modules/fxratesapi/rates: get: tags: - Modules summary: Get all exchange rates description: Get all available exchange rates operationId: getAllExchangeRates responses: '200': description: Exchange rates retrieved successfully /modules/entra/users: get: tags: - Modules summary: List Microsoft Entra users description: Get list of users from Microsoft Entra (Azure AD) operationId: listEntraUsers responses: '200': description: Entra users retrieved successfully # Attachments Endpoints /attachments/upload: post: tags: - Attachments summary: Upload attachment description: Upload a file attachment operationId: uploadAttachment requestBody: required: true content: multipart/form-data: schema: type: object properties: file: type: string format: binary responses: '201': description: Attachment uploaded successfully '400': $ref: '#/components/responses/BadRequest' /orders/attachments: get: tags: - Attachments summary: List order attachments description: Get attachments for an order operationId: listOrderAttachments parameters: - name: order_id in: query required: true schema: type: integer responses: '200': description: Order attachments retrieved successfully /orders/attachments/upload: post: tags: - Attachments summary: Upload order attachment description: Upload an attachment to an order operationId: uploadOrderAttachment requestBody: required: true content: multipart/form-data: schema: type: object properties: order_id: type: integer file: type: string format: binary responses: '201': description: Order attachment uploaded successfully /orders/attachments/download: get: tags: - Attachments summary: Download order attachment description: Download a specific order attachment operationId: downloadOrderAttachment parameters: - name: id in: query required: true schema: type: integer responses: '200': description: Attachment downloaded successfully content: application/octet-stream: schema: type: string format: binary # Forms Endpoints /form: get: tags: - Forms summary: Get form description: Retrieve a form definition operationId: getForm parameters: - name: id in: query schema: type: integer responses: '200': description: Form retrieved successfully post: tags: - Forms summary: Submit form description: Submit a form operationId: submitForm requestBody: required: true content: application/json: schema: type: object properties: form_id: type: integer data: type: object responses: '201': description: Form submitted successfully # Permissions Endpoints /permissions: get: tags: - Users summary: List permissions description: Get list of all available permissions operationId: listPermissions responses: '200': description: Permissions retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/Permission' # Customer Management Endpoints /customer/attributes: get: tags: - Users summary: Get customer attributes description: Get custom attributes for a customer operationId: getCustomerAttributes parameters: - name: customer_id in: query schema: type: integer responses: '200': description: Customer attributes retrieved successfully post: tags: - Users summary: Add customer attribute description: Add a custom attribute to a customer operationId: addCustomerAttribute responses: '201': description: Customer attribute added successfully delete: tags: - Users summary: Delete customer attribute description: Remove a custom attribute from a customer operationId: deleteCustomerAttribute responses: '200': description: Customer attribute deleted successfully /customer/notes: get: tags: - Users summary: Get customer notes description: Get notes for a customer operationId: getCustomerNotes parameters: - name: customer_id in: query schema: type: integer responses: '200': description: Customer notes retrieved successfully post: tags: - Users summary: Add customer note description: Add a note to a customer operationId: addCustomerNote responses: '201': description: Customer note added successfully delete: tags: - Users summary: Delete customer note description: Remove a note from a customer operationId: deleteCustomerNote responses: '200': description: Customer note deleted successfully /customers/search: post: tags: - Users summary: Search customers description: Search for customers using various criteria operationId: searchCustomers requestBody: required: true content: application/json: schema: type: object properties: query: type: string responses: '200': description: Customers found successfully # Configuration Endpoints /economic/config: get: tags: [Config] summary: Get e-conomic config operationId: getEconomicConfig responses: '200': {description: Success} post: tags: [Config] summary: Update e-conomic config operationId: updateEconomicConfig responses: '200': {description: Success} /reCAPTCHA/config: get: tags: [Config] summary: Get reCAPTCHA config operationId: getRecaptchaConfig responses: '200': {description: Success} post: tags: [Config] summary: Update reCAPTCHA config operationId: updateRecaptchaConfig responses: '200': {description: Success} /email/config: get: tags: [Config] summary: Get email config operationId: getEmailConfig responses: '200': {description: Success} post: tags: [Config] summary: Update email config operationId: updateEmailConfig responses: '200': {description: Success} /email/config/test: post: tags: [Config] summary: Test email config operationId: testEmailConfig responses: '200': {description: Success} /backups/config: get: tags: [Config] summary: Get backups config operationId: getBackupsConfig responses: '200': {description: Success} post: tags: [Config] summary: Update backups config operationId: updateBackupsConfig responses: '200': {description: Success} /motorapi/config: get: tags: [Config] summary: Get MotorAPI config operationId: getMotorApiConfig responses: '200': {description: Success} post: tags: [Config] summary: Update MotorAPI config operationId: updateMotorApiConfig responses: '200': {description: Success} /stripe/config: get: tags: [Config] summary: Get Stripe config operationId: getStripeConfig responses: '200': {description: Success} post: tags: [Config] summary: Update Stripe config operationId: updateStripeConfig responses: '200': {description: Success} /fxratesapi/config: get: tags: [Config] summary: Get FXRatesAPI config operationId: getFxRatesApiConfig responses: '200': {description: Success} post: tags: [Config] summary: Update FXRatesAPI config operationId: updateFxRatesApiConfig responses: '200': {description: Success} /gatewayapi/config: get: tags: [Config] summary: Get GatewayAPI config operationId: getGatewayApiConfig responses: '200': {description: Success} post: tags: [Config] summary: Update GatewayAPI config operationId: updateGatewayApiConfig responses: '200': {description: Success} /xlvask/config: get: tags: [Config] summary: Get XLVask config operationId: getXlvaskConfig responses: '200': {description: Success} post: tags: [Config] summary: Update XLVask config operationId: updateXlvaskConfig responses: '200': {description: Success} /entra/config: get: tags: [Config] summary: Get Entra config operationId: getEntraConfig responses: '200': {description: Success} post: tags: [Config] summary: Update Entra config operationId: updateEntraConfig responses: '200': {description: Success} /limble/config: get: tags: [Config] summary: Get Limble config operationId: getLimbleConfig responses: '200': {description: Success} post: tags: [Config] summary: Update Limble config operationId: updateLimbleConfig responses: '200': {description: Success} /ocrspace/config: get: tags: [Config] summary: Get OcrSpace config operationId: getOcrSpaceConfig responses: '200': {description: Success} post: tags: [Config] summary: Update OcrSpace config operationId: updateOcrSpaceConfig responses: '200': {description: Success} /openai/config: get: tags: [Config] summary: Get OpenAI config operationId: getOpenAiConfig responses: '200': {description: Success} post: tags: [Config] summary: Update OpenAI config operationId: updateOpenAiConfig responses: '200': {description: Success} /licenseplaterecognizer/config: get: tags: [Config] summary: Get LicensePlateRecognizer config operationId: getLicensePlateRecognizerConfig responses: '200': {description: Success} post: tags: [Config] summary: Update LicensePlateRecognizer config operationId: updateLicensePlateRecognizerConfig responses: '200': {description: Success} /virkdata/config: get: tags: [Config] summary: Get Virkdata config operationId: getVirkdataConfig responses: '200': {description: Success} post: tags: [Config] summary: Update Virkdata config operationId: updateVirkdataConfig responses: '200': {description: Success} /shelly/config: get: tags: [Config] summary: Get Shelly config operationId: getShellyConfig responses: '200': {description: Success} post: tags: [Config] summary: Update Shelly config operationId: updateShellyConfig responses: '200': {description: Success} /selfserve/config: get: tags: [Config] summary: Get Self-Serve config operationId: getSelfServeConfig responses: '200': description: Self-serve configuration retrieved successfully content: application/json: schema: $ref: '#/components/schemas/SelfServeConfig' post: tags: [Config] summary: Update Self-Serve config operationId: updateSelfServeConfig requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SelfServeConfig' responses: '200': description: Self-serve configuration updated successfully content: application/json: schema: $ref: '#/components/schemas/SelfServeConfig' /branding: get: tags: - Branding summary: List branding options description: Retrieve a list of branding options or a specific branding option if ID is provided operationId: listBrandingOptions parameters: - name: id in: query required: false schema: type: integer - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' responses: '200': description: Branding options retrieved successfully '400': $ref: '#/components/responses/BadRequest' '403': $ref: '#/components/responses/Forbidden' post: tags: - Branding summary: Add branding option description: Create a new branding option operationId: addBrandingOption requestBody: required: true content: application/json: schema: type: object required: [name, description, cvr] properties: name: {type: string} description: {type: string} cvr: {type: integer} responses: '200': description: Branding option added successfully '403': $ref: '#/components/responses/Forbidden' put: tags: - Branding summary: Edit branding option description: Update an existing branding option operationId: editBrandingOption requestBody: required: true content: application/json: schema: type: object required: [id] properties: id: {type: integer} name: {type: string} description: {type: string} cvr: {type: integer} responses: '200': description: Branding option updated successfully '403': $ref: '#/components/responses/Forbidden' /roles: get: tags: - Roles summary: List roles operationId: listRoles responses: '200': description: Success post: tags: - Roles summary: Add role operationId: addRole requestBody: required: true content: application/json: schema: type: object required: [name] properties: name: {type: string} responses: '200': description: Success put: tags: - Roles summary: Edit role operationId: editRole requestBody: required: true content: application/json: schema: type: object required: [id, name] properties: id: {type: integer} name: {type: string} responses: '200': description: Success /roles/permissions: post: tags: - Roles summary: Add permission to role operationId: addRolePermission requestBody: required: true content: application/json: schema: type: object required: [role_id, permission] properties: role_id: {type: integer} permission: {type: string} responses: '200': description: Success delete: tags: - Roles summary: Remove permission from role operationId: removeRolePermission parameters: - name: role_id in: query required: true schema: {type: integer} - name: permission in: query required: true schema: {type: string} responses: '200': description: Success /roles/clone: post: tags: - Roles summary: Clone role operationId: cloneRole requestBody: required: true content: application/json: schema: type: object required: [role_id, name] properties: role_id: {type: integer} name: {type: string} responses: '200': description: Success /modules/washcertificates: get: tags: - Modules summary: List wash certificates operationId: listWashCertificates responses: '200': description: Success /modules/xlvask/services/usage/orders: get: tags: - Modules summary: Get XLVask usage orders operationId: getXlvaskUsageOrders responses: '200': description: Success /modules/xlvask/services/usage/orders/fast-link: get: tags: - Modules summary: Get XLVask usage orders fast link operationId: getXlvaskUsageOrdersFastLink responses: '200': description: Success /superuser/department: get: tags: - Departments summary: List departments (superuser) operationId: listSuperuserDepartments responses: '200': description: Success /superuser/department/prices: get: tags: - Departments summary: Get department prices operationId: getDepartmentPrices parameters: - name: department_id in: query required: true schema: {type: integer} responses: '200': description: Success post: tags: - Departments summary: Set department price operationId: setDepartmentPrice requestBody: required: true content: application/json: schema: type: object required: [department_id, product_id, price] properties: department_id: {type: integer} product_id: {type: integer} price: {type: integer} responses: '200': description: Success /superuser/department/variables: get: tags: - Departments summary: Get department variables operationId: getDepartmentVariables parameters: - name: department_id in: query required: true schema: {type: integer} responses: '200': description: Success post: tags: - Departments summary: Set department variable operationId: setDepartmentVariable requestBody: required: true content: application/json: schema: type: object required: [department_id, variable, value] properties: department_id: {type: integer} variable: {type: string} value: {type: string} responses: '200': description: Success /departments/daily-reports: get: tags: - Departments summary: List daily reports operationId: listDailyReports responses: '200': description: Success post: tags: - Departments summary: Add daily report operationId: addDailyReport requestBody: required: true content: application/json: schema: type: object required: [department_id, date, report] properties: department_id: {type: integer} date: {type: string} report: {type: string} responses: '200': description: Success put: tags: - Departments summary: Edit daily report operationId: editDailyReport requestBody: required: true content: application/json: schema: type: object required: [id, report] properties: id: {type: integer} report: {type: string} responses: '200': description: Success /departments/daily-reports/get: get: tags: - Departments summary: Get daily report operationId: getDailyReport parameters: - name: department_id in: query required: true schema: {type: integer} - name: date in: query required: true schema: {type: string} responses: '200': description: Success /departments/daily-reports/product-count: get: tags: - Departments summary: Get product count for daily reports operationId: getDailyReportProductCount parameters: - name: date in: query required: true schema: {type: string} - name: date_to in: query required: false schema: {type: string} - name: department_id in: query required: true schema: {type: integer} - name: product_id in: query required: true schema: {type: integer} responses: '200': description: Success /departments/daily-reports/transaction-count: get: tags: - Departments summary: Get transaction count for daily reports operationId: getDailyReportTransactionCount parameters: - name: date in: query required: true schema: {type: string} - name: date_to in: query required: false schema: {type: string} - name: department_id in: query required: true schema: {type: integer} responses: '200': description: Success /departments/daily-reports/bookings-count: get: tags: - Departments summary: Get bookings count for daily reports operationId: getDailyReportBookingsCount parameters: - name: date in: query required: true schema: {type: string} - name: department_id in: query required: true schema: {type: integer} responses: '200': description: Success components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: JWT description: JWT token obtained from /auth/login or /auth/employee/login parameters: PageParam: name: page in: query description: Page number for pagination schema: type: integer minimum: 1 default: 1 PerPageParam: name: per_page in: query description: Number of items per page schema: type: integer minimum: 1 maximum: 100 default: 30 SearchParam: name: search in: query description: Search query string schema: type: string LimitParam: name: limit in: query description: Number of items per page schema: type: integer minimum: 1 maximum: 1000 default: 100 FiltersParam: name: filters in: query description: Filters for the list (e.g., module:selfserve,status_code:200) schema: type: string responses: BadRequest: description: Bad request - Invalid input parameters content: application/json: schema: $ref: '#/components/schemas/Error' Unauthorized: description: Unauthorized - Invalid or missing authentication token content: application/json: schema: $ref: '#/components/schemas/Error' Forbidden: description: Forbidden - Insufficient permissions content: application/json: schema: $ref: '#/components/schemas/Error' NotFound: description: Not found - Resource does not exist content: application/json: schema: $ref: '#/components/schemas/Error' InternalServerError: description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' schemas: Error: type: object properties: error: type: string description: Error message code: type: integer description: HTTP status code User: type: object properties: id: type: integer description: User ID customer_number: type: integer description: e-conomic customer number display_name: type: string description: User's display name group_id: type: integer description: User group/role ID phone_country_code: type: integer description: Phone country code phone: type: integer description: Phone number email: type: string format: email description: Email address sms_notifications_enabled: type: boolean description: SMS notifications enabled email_notifications_enabled: type: boolean description: Email notifications enabled created_at: type: string format: date-time updated_at: type: string format: date-time UserCreate: type: object required: - customer_number - password properties: customer_number: type: integer password: type: string format: password display_name: type: string group_id: type: integer email: type: string format: email phone: type: integer phone_country_code: type: integer UserUpdate: type: object properties: id: type: integer customer_number: type: integer display_name: type: string group_id: type: integer email: type: string format: email phone: type: integer phone_country_code: type: integer Order: type: object properties: id: type: integer description: Order ID customer_id: type: integer description: Customer number customer_name: type: string description: Customer name user_id: type: integer description: User ID cashier_id: type: integer description: Cashier user ID cashier_name: type: string description: Cashier name department_id: type: integer description: Department ID status: type: string description: Order status total_net_amount: type: number format: float description: Total order amount po: type: string description: Purchase order number nullable: true lane: type: string description: Lane information nullable: true created_at: type: string format: date-time updated_at: type: string format: date-time SelfServeLaneStatus: type: object properties: id: type: integer description: Lane ID status: type: string description: Current lane status (e.g., IDLE, OCCUPIED) mode: type: string description: Current lane mode (e.g., AUTOMATIC, MANUAL) state: type: string description: Current lane state (e.g., READY, WASHING) wash_start_time: type: integer description: Timestamp when the wash started (0 if not washing) nullable: true elapsed_wash_time: type: integer description: Elapsed wash time in seconds nullable: true license_plate: type: string description: License plate of the vehicle in the lane nullable: true customer_number: type: integer description: Customer number associated with the current lane use nullable: true SelfServeConfig: type: object properties: enabled: type: boolean description: Whether the self-serve module is enabled minute_product: type: integer description: The product ID used for minute-based billing DepartmentSelfserveQuestion: type: object properties: id: type: integer description: Question ID department: type: integer description: Department ID lane: type: integer description: Lane ID product: type: integer description: Product ID condition_id: type: integer description: Question condition object ID nullable: true question: type: string description: Question text description: type: string description: Question description order_priority: type: integer description: Display order priority (lower numbers shown first) created_at: type: string format: date-time updated_at: type: string format: date-time DepartmentSelfserveTask: type: object properties: id: type: integer description: Task ID department: type: integer description: Department ID lane: type: integer description: Lane ID product: type: integer description: Product ID condition_id: type: integer description: Condition ID (if conditional task) nullable: true task: type: string description: Task text description: type: string description: Task description order_priority: type: integer description: Display order priority (lower numbers shown first) created_at: type: string format: date-time updated_at: type: string format: date-time DepartmentSelfserveCondition: type: object properties: id: type: integer description: Condition ID department: type: integer description: Department ID lane: type: integer description: Lane ID product: type: integer description: Product ID condition_id: type: integer description: Optional condition ID nullable: true name: type: string description: Condition name description: type: string description: Condition description created_at: type: string format: date-time updated_at: type: string format: date-time DepartmentSelfserveConditionRule: type: object properties: id: type: integer description: Rule ID condition_id: type: integer description: Condition object ID type: type: string description: Condition type (e.g., IS_TRUE, IS_FALSE) object_type: type: string description: The object type to which the condition applies (e.g., question, task, etc.) object_id: type: integer description: The object id to which the condition applies name: type: string description: Condition name description: type: string description: Condition description created_at: type: string format: date-time updated_at: type: string format: date-time DepartmentSelfserveVehicleCondition: type: object properties: id: type: integer description: Vehicle condition ID department: type: integer description: Department ID lane: type: integer description: Lane ID customer_id: type: integer description: Customer ID nullable: true reg: type: string description: Vehicle registration number question: type: integer description: Question ID value: type: boolean description: Answer value created_at: type: string format: date-time updated_at: type: string format: date-time deleted_at: type: string format: date-time nullable: true OrderCreate: type: object required: - customer_id - department_id properties: customer_id: type: integer department_id: type: integer cashier_id: type: integer po: type: string lane: type: string OrderUpdate: type: object properties: id: type: integer customer_id: type: integer department_id: type: integer status: type: string po: type: string lane: type: string OrderItem: type: object properties: id: type: integer order_id: type: integer product_id: type: integer product_name: type: string quantity: type: integer unit_price: type: number format: float discount: type: number format: float total_price: type: number format: float OrderItemCreate: type: object required: - order_id - product_id - quantity properties: order_id: type: integer product_id: type: integer quantity: type: integer discount: type: number format: float OrderItemUpdate: type: object required: - id properties: id: type: integer quantity: type: integer discount: type: number format: float Department: type: object properties: id: type: integer name: type: string description: type: string economic_department_id: type: integer visible: type: boolean dimension: type: integer branding: type: integer longitude: type: number format: float latitude: type: number format: float created_at: type: string format: date-time updated_at: type: string format: date-time DepartmentCreate: type: object required: - name - economic_department_id properties: name: type: string description: type: string economic_department_id: type: integer visible: type: boolean longitude: type: number format: float latitude: type: number format: float DepartmentUpdate: type: object required: - id properties: id: type: integer name: type: string description: type: string visible: type: boolean longitude: type: number format: float latitude: type: number format: float DepartmentLane: type: object properties: id: type: integer department: type: integer name: type: string relay_in_id: type: string relay_out_id: type: string relay_machine_id: type: string created_at: type: string format: date-time updated_at: type: string format: date-time DepartmentLaneCreate: type: object required: - department - name properties: department: type: integer name: type: string relay_in_id: type: string relay_out_id: type: string relay_machine_id: type: string DepartmentLaneUpdate: type: object required: - id properties: id: type: integer department: type: integer name: type: string relay_in_id: type: string relay_out_id: type: string relay_machine_id: type: string Product: type: object properties: id: type: integer name: type: string description: type: string price: type: number format: float category_id: type: integer category_name: type: string visible: type: boolean created_at: type: string format: date-time updated_at: type: string format: date-time ProductCreate: type: object required: - name - price - category_id properties: name: type: string description: type: string price: type: number format: float category_id: type: integer visible: type: boolean ProductUpdate: type: object required: - id properties: id: type: integer name: type: string description: type: string price: type: number format: float category_id: type: integer visible: type: boolean Category: type: object properties: id: type: integer name: type: string description: type: string created_at: type: string format: date-time updated_at: type: string format: date-time CategoryCreate: type: object required: - name properties: name: type: string description: type: string CategoryUpdate: type: object required: - id properties: id: type: integer name: type: string description: type: string ModuleActionLog: type: object properties: id: type: integer description: Log ID module: type: string description: The module name action: type: string description: The action name status_code: type: integer description: HTTP status code data: type: object description: The action data (JSON decoded) created_at: type: string format: date-time description: Log creation timestamp Booking: type: object properties: id: type: integer customer_id: type: integer department_id: type: integer booking_time: type: string format: date-time status: type: string created_at: type: string format: date-time BookingUpdate: type: object required: - id properties: id: type: integer status: type: string booking_time: type: string format: date-time Vehicle: type: object properties: id: type: integer plate: type: string customer_id: type: integer created_at: type: string format: date-time Notification: type: object properties: id: type: integer user_id: type: integer title: type: string message: type: string read: type: boolean created_at: type: string format: date-time NotificationCreate: type: object required: - user_id - title - message properties: user_id: type: integer title: type: string message: type: string Permission: type: object properties: name: type: string description: Permission identifier description: type: string description: Human-readable description GoalsCriteria: type: object description: Goal evaluation criteria properties: type: type: string description: Criteria type (e.g., PRODUCT) example: PRODUCT target: type: number description: Target value for the goal example: 100 start: type: string format: date-time description: Start of the evaluation window (ISO 8601) end: type: string format: date-time description: End of the evaluation window (ISO 8601) users: type: array description: List of user customer numbers included in the criteria items: { type: integer } departments: type: array description: List of department IDs included in the criteria items: { type: integer } products: type: array description: List of product IDs included in the criteria items: { type: integer } DepartmentGoal: type: object properties: id: type: integer created_by: type: integer description: ID of the user who created the goal departments: type: array items: { type: integer } criteria: $ref: '#/components/schemas/GoalsCriteria' created_at: type: string description: Creation timestamp updated_at: type: string description: Update timestamp DepartmentGoalCreate: type: object required: [departments, criteria] properties: departments: type: array items: { type: integer } criteria: $ref: '#/components/schemas/GoalsCriteria' DepartmentGoalUpdate: type: object required: [id] properties: id: type: integer departments: type: array items: { type: integer } criteria: $ref: '#/components/schemas/GoalsCriteria'