{ "openapi": "3.0.3", "info": { "title": "Copenhagen Truck Wash API", "description": "This API provides access to the Copenhagen Truck Wash system, managing orders, bookings, \ndepartments, products, customers, and various integrations including e-conomic, Stripe, \nXLVask, and more.\n\n## Authentication\nMost endpoints require authentication using a Bearer token obtained from the `/auth/login` \nor `/auth/employee/login` endpoints.\n\n## Permissions\nMany endpoints require specific permissions that are assigned to user groups/roles.\n\n## Subusers and customer targeting\nWhen authenticated as a subuser, most customer-scoped endpoints require an explicit target\ncustomer context. Provide the header `X-Customer-Number: ` to target a\nspecific customer. If omitted, the API attempts to infer the customer from the authenticated\nuser context when possible. Classic user sessions ignore this header.\n", "version": "1.0.0", "contact": { "name": "Copenhagen Truck Wash", "email": "support@truckwash.dk" } }, "servers": [ { "url": "https://api.truckwash.dk", "description": "Production server (.dk)" }, { "url": "https://api.truckwash.io", "description": "Production server (.io)" }, { "url": "http://localhost/api", "description": "Local development server" } ], "security": [ { "BearerAuth": [] } ], "tags": [ { "name": "Authentication", "description": "User and employee authentication endpoints" }, { "name": "Security", "description": "Account security and passkey management endpoints" }, { "name": "Users", "description": "User management and customer operations" }, { "name": "Search", "description": "System-wide search endpoints" }, { "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" }, { "name": "Subusers", "description": "Subuser registration and setup" }, { "name": "Bird", "description": "Voice Calls via Bird" } ], "paths": { "/bird/voice/calls": { "post": { "tags": [ "Bird" ], "summary": "Create/place a voice call via Bird", "operationId": "birdCreateVoiceCall", "parameters": [ { "in": "query", "name": "workspaceId", "schema": { "type": "string" }, "required": false, "description": "Bird Workspace identifier (falls back to module configuration if omitted)" }, { "in": "query", "name": "channelId", "schema": { "type": "string" }, "required": false, "description": "Bird Channel identifier (falls back to module configuration if omitted)" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true, "properties": { "to": { "type": "string", "description": "E.164 phone number of the callee", "example": "+4511122233" }, "from": { "type": "string", "description": "E.164 phone number of the caller (sender)", "example": "+4599988877" } } } } } }, "responses": { "200": { "description": "Call created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BirdVoiceCallSingleResponse" } } } } } }, "get": { "tags": [ "Bird" ], "summary": "List voice calls", "operationId": "birdListVoiceCalls", "parameters": [ { "in": "query", "name": "workspaceId", "schema": { "type": "string" }, "required": false, "description": "Bird Workspace identifier (falls back to module configuration if omitted)" }, { "in": "query", "name": "channelId", "schema": { "type": "string" }, "required": false, "description": "Bird Channel identifier (falls back to module configuration if omitted)" }, { "in": "query", "name": "page", "schema": { "type": "integer" }, "required": false } ], "responses": { "200": { "description": "A list of calls", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BirdVoiceCallListResponse" } } } } } } }, "/bird/voice/calls/{id}": { "get": { "tags": [ "Bird" ], "summary": "Get a voice call by ID", "operationId": "birdGetVoiceCall", "parameters": [ { "in": "query", "name": "workspaceId", "schema": { "type": "string" }, "required": false, "description": "Bird Workspace identifier (falls back to module configuration if omitted)" }, { "in": "query", "name": "channelId", "schema": { "type": "string" }, "required": false, "description": "Bird Channel identifier (falls back to module configuration if omitted)" }, { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Call details", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BirdVoiceCallSingleResponse" } } } } } } }, "/bird/voice/calls/{id}/hangup": { "post": { "tags": [ "Bird" ], "summary": "Hang up a voice call by ID", "operationId": "birdHangupVoiceCall", "parameters": [ { "in": "query", "name": "workspaceId", "schema": { "type": "string" }, "required": false, "description": "Bird Workspace identifier (falls back to module configuration if omitted)" }, { "in": "query", "name": "channelId", "schema": { "type": "string" }, "required": false, "description": "Bird Channel identifier (falls back to module configuration if omitted)" }, { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Hangup requested", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BirdVoiceCallSingleResponse" } } } } } } }, "/bird/voice/calls/{id}/say": { "post": { "tags": [ "Bird" ], "summary": "Say a message on an active voice call and hang up afterwards", "operationId": "birdSayOnVoiceCall", "parameters": [ { "in": "query", "name": "workspaceId", "schema": { "type": "string" }, "required": false, "description": "Bird Workspace identifier (falls back to module configuration if omitted)" }, { "in": "query", "name": "channelId", "schema": { "type": "string" }, "required": false, "description": "Bird Channel identifier (falls back to module configuration if omitted)" }, { "in": "path", "name": "id", "required": true, "schema": { "type": "string" }, "description": "Call identifier" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "text" ], "properties": { "text": { "type": "string", "description": "The text message to play via TTS", "example": "The gate will open shortly." }, "locale": { "type": "string", "description": "The locale to use for the TTS voice (e.g. en-US)", "example": "en-US" }, "voice": { "type": "string", "description": "The voice identifier to use", "example": "male" }, "loop": { "type": "integer", "description": "Number of times to loop the message", "example": 1 }, "timeout": { "type": "integer", "description": "Timeout in seconds for the TTS action", "example": 1 }, "hangup": { "type": "boolean", "description": "Whether to hang up the call after the message finishes playing (defaults to true)", "example": true } } } } } }, "responses": { "200": { "description": "TTS action requested", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BirdVoiceCallSingleResponse" } } } } } } }, "/bird/voice/calls/test-outbound": { "post": { "tags": [ "Bird" ], "summary": "Place a test outbound call and hang up when accepted", "operationId": "birdTestOutboundVoiceCall", "description": "Calls +45 42 33 11 28 and hangs up when the call reaches accepted/ongoing state.", "parameters": [ { "in": "query", "name": "workspaceId", "schema": { "type": "string" }, "required": false, "description": "Bird Workspace identifier (falls back to module configuration if omitted)" }, { "in": "query", "name": "channelId", "schema": { "type": "string" }, "required": false, "description": "Bird Channel identifier (falls back to module configuration if omitted)" } ], "requestBody": { "required": false, "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true, "properties": { "from": { "type": "string", "description": "Caller E.164 number to use for the test call", "example": "+4599988877" }, "pollIntervalSeconds": { "type": "integer", "minimum": 1, "description": "Poll interval while waiting for accepted status", "example": 2 }, "maxPollSeconds": { "type": "integer", "minimum": 5, "description": "Max time to wait before timing out", "example": 30 }, "hangupCause": { "type": "string", "description": "Optional hangup cause passed through to Bird" } } } } } }, "responses": { "200": { "description": "Test call created and either hung up or timed out", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BirdTestOutboundCallResponse" } } } } } } }, "/bird/numbers": { "get": { "tags": [ "Bird" ], "summary": "List your numbers", "operationId": "birdListNumbers", "parameters": [ { "in": "query", "name": "workspaceId", "required": false, "schema": { "type": "string" }, "description": "Bird Workspace identifier (optional if configured)" }, { "in": "query", "name": "page", "required": false, "schema": { "type": "integer" } }, { "in": "query", "name": "limit", "required": false, "schema": { "type": "integer" } } ], "responses": { "200": { "description": "A list of numbers", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BirdNumberListResponse" } } } } } } }, "/bird/numbers/{id}": { "get": { "tags": [ "Bird" ], "summary": "Get a number by ID", "operationId": "birdGetNumber", "parameters": [ { "in": "query", "name": "workspaceId", "required": false, "schema": { "type": "string" }, "description": "Bird Workspace identifier (optional if configured)" }, { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Number details", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BirdNumberSingleResponse" } } } } } }, "delete": { "tags": [ "Bird" ], "summary": "Delete/release a number by ID", "operationId": "birdDeleteNumber", "parameters": [ { "in": "query", "name": "workspaceId", "required": false, "schema": { "type": "string" }, "description": "Bird Workspace identifier (optional if configured)" }, { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Number deletion/release accepted", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } } } } } }, "/bird/voice/flash-calls": { "post": { "tags": [ "Bird" ], "summary": "Create/place a flash call via Bird", "operationId": "birdCreateFlashCall", "parameters": [ { "in": "query", "name": "workspaceId", "schema": { "type": "string" }, "required": false, "description": "Bird Workspace identifier (falls back to module configuration if omitted)" }, { "in": "query", "name": "channelId", "schema": { "type": "string" }, "required": false, "description": "Bird Channel identifier (falls back to module configuration if omitted)" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } } }, "responses": { "200": { "description": "Flash call created", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } } } } }, "get": { "tags": [ "Bird" ], "summary": "List flash calls", "operationId": "birdListFlashCalls", "parameters": [ { "in": "query", "name": "workspaceId", "schema": { "type": "string" }, "required": false, "description": "Bird Workspace identifier (falls back to module configuration if omitted)" }, { "in": "query", "name": "channelId", "schema": { "type": "string" }, "required": false, "description": "Bird Channel identifier (falls back to module configuration if omitted)" }, { "in": "query", "name": "page", "schema": { "type": "integer" }, "required": false } ], "responses": { "200": { "description": "A list of flash calls", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } } } } } }, "/bird/voice/flash-calls/{id}": { "get": { "tags": [ "Bird" ], "summary": "Get a flash call by ID", "operationId": "birdGetFlashCall", "parameters": [ { "in": "query", "name": "workspaceId", "schema": { "type": "string" }, "required": false, "description": "Bird Workspace identifier (falls back to module configuration if omitted)" }, { "in": "query", "name": "channelId", "schema": { "type": "string" }, "required": false, "description": "Bird Channel identifier (falls back to module configuration if omitted)" }, { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Flash call details", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } } } } }, "post": { "tags": [ "Bird" ], "summary": "Complete/end a flash call by ID", "description": "Posts a completion/update payload to the flash call resource to finalize verification.", "operationId": "birdEndFlashCall", "parameters": [ { "in": "query", "name": "workspaceId", "schema": { "type": "string" }, "required": false, "description": "Bird Workspace identifier (falls back to module configuration if omitted)" }, { "in": "query", "name": "channelId", "schema": { "type": "string" }, "required": false, "description": "Bird Channel identifier (falls back to module configuration if omitted)" }, { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": false, "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } } }, "responses": { "200": { "description": "Flash call completed", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } } } } } }, "/bird/voice/flash-calls/end": { "post": { "tags": [ "Bird" ], "summary": "Complete/end a flash call using from/to numbers", "description": "Ends an ongoing flash call by specifying the originating and destination numbers.", "operationId": "birdEndFlashCallByNumbers", "parameters": [ { "in": "query", "name": "workspaceId", "schema": { "type": "string" }, "required": false, "description": "Bird Workspace identifier (falls back to module configuration if omitted)" }, { "in": "query", "name": "channelId", "schema": { "type": "string" }, "required": false, "description": "Bird Channel identifier (falls back to module configuration if omitted)" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true, "properties": { "from": { "type": "string", "description": "E.164 formatted caller number", "example": "+4599988877" }, "to": { "type": "string", "description": "E.164 formatted callee number", "example": "+4511122233" } } } } } }, "responses": { "200": { "description": "Flash call completed (by numbers)", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } } } } } }, "/subusers": { "get": { "tags": [ "Subusers" ], "summary": "List subusers visible to the authenticated user", "description": "Returns a paginated list of subusers (drivers) that have enabled grants tied to the\nauthenticated user's customer number. Only subusers with at least one enabled, non-deleted\ngrant for the caller's customer are returned.\n", "operationId": "listSubusers", "parameters": [ { "name": "page", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1 } }, { "name": "limit", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "maximum": 1000 } }, { "name": "search", "in": "query", "required": false, "schema": { "type": "string" } }, { "name": "include_non_enabled", "in": "query", "required": false, "description": "Include subusers that only have non-enabled grants (default false)", "schema": { "type": "boolean" } } ], "responses": { "200": { "description": "List of visible subusers", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer" }, "username": { "type": "string", "nullable": true }, "name": { "type": "string", "nullable": true }, "email": { "type": "string", "format": "email", "nullable": true }, "phone_country_code": { "type": "integer", "nullable": true }, "phone": { "type": "integer", "nullable": true }, "created_at": { "type": "string", "format": "date-time", "nullable": true }, "updated_at": { "type": "string", "format": "date-time", "nullable": true }, "suspended_at": { "type": "string", "format": "date-time", "nullable": true }, "two_factor_enabled": { "type": "boolean", "description": "Indicates if 2FA is enabled for this account" }, "permissions": { "type": "array", "description": "Aggregated permission keys granted for the caller's customer", "items": { "type": "string" } } } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/subusers/me": { "get": { "tags": [ "Subusers" ], "summary": "Get current subuser profile", "description": "Returns the authenticated subuser (driver) profile and their enabled grants grouped by\n`billing_customer_number`.\n\nNotes:\n- This endpoint is available only to authenticated subuser sessions.\n- It does not require the `X-Customer-Number` header; all enabled, non-deleted grants for the\n subuser are included in the response.\n", "operationId": "getCurrentSubuser", "responses": { "200": { "description": "Current subuser details", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubuserSelf" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "500": { "$ref": "#/components/responses/InternalServerError" } } }, "post": { "tags": [ "Subusers" ], "summary": "Create a subuser registration", "description": "Creates a subuser (driver) account using a company's CVR and a phone number. Validates the\nCVR via e-conomic, ensures the phone number is not already in use, and if SMS is enabled\nsends a setup link by SMS for the user to complete registration.\n", "operationId": "createSubuser", "security": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "cvr", "phone_country_code", "phone" ], "properties": { "cvr": { "type": "integer", "description": "Danish CVR (8 digits)", "example": 12345678 }, "phone_country_code": { "type": "integer", "description": "Phone country code (1–3 digits)", "example": 45 }, "phone": { "type": "integer", "description": "Phone number (4–15 digits, no leading +)", "example": 12345678 } } } } } }, "responses": { "200": { "description": "Subuser created (or pending setup) and company identified", "content": { "application/json": { "schema": { "type": "object", "properties": { "cvr": { "type": "integer", "example": 12345678 }, "customer_number": { "type": "integer", "description": "Matched e-conomic customer number", "example": 1000 } } } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "404": { "$ref": "#/components/responses/NotFound" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/subusers/{id}": { "get": { "tags": [ "Subusers" ], "summary": "Get a subuser by ID (visible by grant)", "description": "Returns the subuser if the authenticated user has at least one enabled, non-deleted grant\nfor their customer number to this subuser. Otherwise returns 404.\n", "operationId": "getSubuser", "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } } ], "responses": { "200": { "description": "Subuser details", "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer" }, "username": { "type": "string", "nullable": true }, "name": { "type": "string", "nullable": true }, "email": { "type": "string", "format": "email", "nullable": true }, "phone_country_code": { "type": "integer", "nullable": true }, "phone": { "type": "integer", "nullable": true }, "created_at": { "type": "string", "format": "date-time", "nullable": true }, "updated_at": { "type": "string", "format": "date-time", "nullable": true }, "suspended_at": { "type": "string", "format": "date-time", "nullable": true }, "permissions": { "type": "array", "description": "Aggregated permission keys granted for the caller's customer", "items": { "type": "string" } } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/subusers/setup": { "get": { "tags": [ "Subusers" ], "summary": "Validate setup token", "description": "Validates a subuser setup token generated during registration.", "operationId": "validateSubuserSetupToken", "security": [], "parameters": [ { "name": "token", "in": "query", "required": true, "schema": { "type": "string" }, "description": "One-time setup token received via SMS" } ], "responses": { "200": { "description": "Token is valid", "content": { "application/json": { "schema": { "type": "object", "properties": { "message": { "type": "string", "example": "Token is valid" }, "subuser_id": { "type": "integer", "example": 42 } } } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "500": { "$ref": "#/components/responses/InternalServerError" } } }, "post": { "tags": [ "Subusers" ], "summary": "Complete subuser setup", "description": "Completes subuser setup by setting a password and basic profile fields. Accepts optional\n`username` and `email`.\n", "operationId": "completeSubuserSetup", "security": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "token", "password", "name" ], "properties": { "token": { "type": "string", "description": "One-time setup token" }, "password": { "type": "string", "format": "password", "minLength": 8, "description": "Must include at least one uppercase letter, one lowercase letter, and one number", "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\\\d).+$" }, "name": { "type": "string", "minLength": 3, "maxLength": 255 }, "username": { "type": "string", "minLength": 3, "maxLength": 255 }, "email": { "type": "string", "format": "email", "minLength": 3, "maxLength": 255 } } } } } }, "responses": { "200": { "description": "Setup completed", "content": { "application/json": { "schema": { "type": "object", "properties": { "message": { "type": "string", "example": "Password set successfully" } } } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/subusers/auth/password": { "post": { "tags": [ "Subusers" ], "summary": "Authenticate subuser with password", "description": "Authenticates a subuser (driver) using a password together with one of the supported\nidentifiers: `phone_country_code` + `phone`, `subuser_id`, or `username`.\n\nOn success, returns a newly generated session token for the subuser.\n", "operationId": "subuserPasswordAuth", "security": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "oneOf": [ { "type": "object", "required": [ "phone_country_code", "phone", "password" ], "properties": { "phone_country_code": { "type": "integer", "description": "Phone country code (1–3 digits)", "minimum": 1, "maximum": 999, "example": 45 }, "phone": { "type": "integer", "description": "Phone number (4–15 digits, no leading +)", "minimum": 1000, "maximum": 999999999999999, "example": 12345678 }, "password": { "type": "string", "format": "password", "minLength": 8, "maxLength": 255 } } }, { "type": "object", "required": [ "subuser_id", "password" ], "properties": { "subuser_id": { "type": "integer", "description": "Subuser ID", "example": 42 }, "password": { "type": "string", "format": "password", "minLength": 8, "maxLength": 255 } } }, { "type": "object", "required": [ "username", "password" ], "properties": { "username": { "type": "string", "minLength": 3, "maxLength": 255, "example": "jdoe" }, "password": { "type": "string", "format": "password", "minLength": 8, "maxLength": 255 } } } ] }, "examples": { "withPhone": { "summary": "Authenticate with phone", "value": { "phone_country_code": 45, "phone": 12345678, "password": "MySecureP@ssw0rd" } }, "withSubuserId": { "summary": "Authenticate with subuser_id", "value": { "subuser_id": 42, "password": "MySecureP@ssw0rd" } }, "withUsername": { "summary": "Authenticate with username", "value": { "username": "jdoe", "password": "MySecureP@ssw0rd" } } } } } }, "responses": { "200": { "description": "Authentication successful", "content": { "application/json": { "schema": { "oneOf": [ { "type": "object", "required": [ "session" ], "properties": { "session": { "type": "string", "description": "Newly generated subuser session token", "example": "2f7a8c0e-9b1d-4c6a-91a9-1a2b3c4d5e6f" } } }, { "type": "object", "required": [ "2fa_required", "2fa_token" ], "properties": { "2fa_required": { "type": "boolean", "example": true }, "2fa_token": { "type": "string", "description": "Temporary 2FA verification token", "example": "557a3e7b1a2b..." } } } ] } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "404": { "$ref": "#/components/responses/NotFound" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/subusers/grants": { "get": { "tags": [ "Subusers" ], "summary": "List subuser grants", "description": "Returns subuser grant records filtered by `customer_number` and/or `subuser_id`.", "operationId": "listSubuserGrants", "security": [ { "BearerAuth": [] } ], "parameters": [ { "name": "customer_number", "in": "query", "required": false, "schema": { "type": "integer" }, "description": "e-conomic customer number to filter by" }, { "name": "subuser_id", "in": "query", "required": false, "schema": { "type": "integer" }, "description": "Subuser ID to filter by" } ], "responses": { "200": { "description": "Grants fetched", "content": { "application/json": { "schema": { "type": "object", "properties": { "grants": { "type": "array", "items": { "$ref": "#/components/schemas/SubuserGrant" } } } } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "500": { "$ref": "#/components/responses/InternalServerError" } } }, "post": { "tags": [ "Subusers" ], "summary": "Create subuser grant", "operationId": "createSubuserGrant", "security": [ { "BearerAuth": [] } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubuserGrantCreateRequest" }, "examples": { "default": { "value": { "customer_number": 1000, "subuser_id": 42, "enabled": true, "note": "Grant for bookings access", "permissions": [ "BOOKINGS_LIST", "BOOKINGS_ADD", "BOOKINGS_EDIT" ] } } } } } }, "responses": { "200": { "description": "Grant created", "content": { "application/json": { "schema": { "type": "object", "properties": { "grant": { "$ref": "#/components/schemas/SubuserGrant" } } } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/subusers/grants/{id}": { "patch": { "tags": [ "Subusers" ], "summary": "Update subuser grant", "operationId": "updateSubuserGrant", "security": [ { "BearerAuth": [] } ], "parameters": [ { "in": "path", "name": "id", "required": true, "schema": { "type": "integer" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubuserGrantUpdateRequest" }, "examples": { "enableOnly": { "value": { "enabled": true } }, "updatePermissions": { "value": { "permissions": [ "VEHICLES_LIST", "SELFSERVE_ADD" ] } } } } } }, "responses": { "200": { "description": "Grant updated", "content": { "application/json": { "schema": { "type": "object", "properties": { "grant": { "$ref": "#/components/schemas/SubuserGrant" } } } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" }, "500": { "$ref": "#/components/responses/InternalServerError" } } }, "delete": { "tags": [ "Subusers" ], "summary": "Delete subuser grant", "operationId": "deleteSubuserGrant", "security": [ { "BearerAuth": [] } ], "parameters": [ { "in": "path", "name": "id", "required": true, "schema": { "type": "integer" } } ], "responses": { "200": { "description": "Grant deleted", "content": { "application/json": { "schema": { "type": "object", "properties": { "message": { "type": "string", "example": "Grant deleted" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/subusers/permission-nodes": { "get": { "tags": [ "Subusers" ], "summary": "List available subuser permission nodes", "description": "Returns grouped permission nodes available for subuser grants.", "operationId": "listSubuserPermissionNodes", "security": [ { "BearerAuth": [] } ], "responses": { "200": { "description": "Permission nodes fetched", "content": { "application/json": { "schema": { "type": "object", "properties": { "permission_nodes": { "type": "array", "items": { "$ref": "#/components/schemas/PermissionNodeGroup" } } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/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", "g_recaptcha_response" ], "properties": { "customer_number": { "type": "integer", "description": "Customer's e-conomic customer number", "example": 12345 }, "password": { "type": "string", "format": "password", "description": "Customer password", "minLength": 1 }, "g_recaptcha_response": { "type": "string", "description": "reCAPTCHA verification token" } } } } } }, "responses": { "200": { "description": "Login successful", "content": { "application/json": { "schema": { "oneOf": [ { "type": "object", "required": [ "token" ], "properties": { "token": { "type": "string", "description": "Bearer authentication token", "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } } }, { "type": "object", "required": [ "2fa_required", "2fa_token" ], "properties": { "2fa_required": { "type": "boolean", "example": true }, "2fa_token": { "type": "string", "description": "Temporary 2FA verification token", "example": "557a3e7b1a2b..." } } } ] } } } }, "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", "g_recaptcha_response" ], "properties": { "user_id": { "type": "integer", "description": "Employee user ID", "example": 1 }, "password": { "type": "string", "format": "password", "description": "Employee password" }, "g_recaptcha_response": { "type": "string", "description": "reCAPTCHA verification token" } } } } } }, "responses": { "200": { "description": "Login successful", "content": { "application/json": { "schema": { "oneOf": [ { "type": "object", "required": [ "token" ], "properties": { "token": { "type": "string", "description": "Bearer authentication token" } } }, { "type": "object", "required": [ "2fa_required", "2fa_token" ], "properties": { "2fa_required": { "type": "boolean", "example": true }, "2fa_token": { "type": "string", "description": "Temporary 2FA verification token" } } } ] } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/auth/passkey/challenge": { "post": { "tags": [ "Authentication" ], "summary": "Initiate passkey authentication challenge", "description": "Generates a WebAuthn PublicKeyCredentialRequestOptions payload. If customer_number is provided, allowCredentials will be populated with existing passkeys for that account. Otherwise, a challenge is issued for discoverable credentials.", "operationId": "passkeyChallenge", "security": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "g_recaptcha_response" ], "properties": { "customer_number": { "type": "integer", "description": "Optional customer's e-conomic customer number", "example": 12345 }, "g_recaptcha_response": { "type": "string", "description": "reCAPTCHA verification token" } } } } } }, "responses": { "200": { "description": "Challenge generated", "content": { "application/json": { "schema": { "type": "object", "properties": { "challenge_token": { "type": "string", "description": "Temporary token binding the challenge to the login attempt" }, "publicKey": { "type": "object", "properties": { "challenge": { "type": "string", "description": "Base64URL-encoded challenge" }, "rpId": { "type": "string", "description": "Relying party ID (truckwash.io or localhost)", "example": "truckwash.io" }, "timeout": { "type": "integer", "description": "Timeout in milliseconds" }, "userVerification": { "type": "string", "enum": [ "required", "preferred", "discouraged" ] }, "allowCredentials": { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string", "example": "public-key" }, "id": { "type": "string", "description": "Base64URL-encoded credential ID" }, "transports": { "type": "array", "items": { "type": "string" } } } } } } } } } } } }, "400": { "$ref": "#/components/responses/BadRequest" } } } }, "/auth/passkey/verify": { "post": { "tags": [ "Authentication" ], "summary": "Verify passkey authentication and start session", "description": "Verifies the WebAuthn assertion and challenge token. Returns a session token on success.", "operationId": "passkeyVerify", "security": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "challenge_token", "credential", "g_recaptcha_response" ], "properties": { "challenge_token": { "type": "string", "description": "The token returned by the challenge endpoint" }, "g_recaptcha_response": { "type": "string", "description": "reCAPTCHA verification token" }, "credential": { "type": "object", "description": "The WebAuthn PublicKeyCredential object (assertion)", "required": [ "id", "rawId", "type", "response" ], "properties": { "id": { "type": "string", "description": "The credential ID (base64url)" }, "rawId": { "type": "string", "description": "The raw credential ID (base64url)" }, "type": { "type": "string", "example": "public-key" }, "clientExtensionResults": { "type": "object" }, "response": { "type": "object", "required": [ "clientDataJSON", "authenticatorData", "signature" ], "properties": { "clientDataJSON": { "type": "string", "description": "Base64URL-encoded client data" }, "authenticatorData": { "type": "string", "description": "Base64URL-encoded authenticator data" }, "signature": { "type": "string", "description": "Base64URL-encoded signature" }, "userHandle": { "type": "string", "nullable": true, "description": "Base64URL-encoded user handle" } } } } } } } } } }, "responses": { "200": { "description": "Verification successful, session started", "content": { "application/json": { "schema": { "oneOf": [ { "type": "object", "required": [ "token" ], "properties": { "token": { "type": "string", "description": "Bearer token for customer" } } }, { "type": "object", "required": [ "session" ], "properties": { "session": { "type": "string", "description": "Session token for subuser" } } } ] } } } }, "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": { "allOf": [ { "$ref": "#/components/schemas/User" }, { "type": "object", "properties": { "two_factor_enabled": { "type": "boolean", "description": "Indicates if 2FA is enabled for this account" } } } ] } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/auth/2fa/setup": { "post": { "tags": [ "Authentication" ], "summary": "Generate 2FA secret", "description": "Generate a new TOTP secret for the authenticated user/subuser", "operationId": "setup2fa", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "2FA secret generated successfully", "content": { "application/json": { "schema": { "type": "object", "properties": { "secret": { "type": "string", "description": "The base32 encoded TOTP secret" }, "qr_code_url": { "type": "string", "description": "An otpauth URL for generating a QR code" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/auth/2fa/enable": { "post": { "tags": [ "Authentication" ], "summary": "Enable 2FA", "description": "Verify a code and enable 2FA for the authenticated user/subuser", "operationId": "enable2fa", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "code" ], "properties": { "code": { "type": "string", "description": "The 6-digit TOTP code" } } } } } }, "responses": { "200": { "description": "2FA enabled successfully", "content": { "application/json": { "schema": {} } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/auth/2fa/disable": { "post": { "tags": [ "Authentication" ], "summary": "Disable 2FA", "description": "Verify a code and disable 2FA for the authenticated user/subuser", "operationId": "disable2fa", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "code" ], "properties": { "code": { "type": "string", "description": "The 6-digit TOTP code" } } } } } }, "responses": { "200": { "description": "2FA disabled successfully", "content": { "application/json": { "schema": {} } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/auth/2fa/verify": { "post": { "tags": [ "Authentication" ], "summary": "Verify 2FA code during login", "description": "Complete the login process by verifying the 2FA code", "operationId": "verify2fa", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "2fa_token", "code" ], "properties": { "2fa_token": { "type": "string", "description": "The temporary 2FA verification token" }, "code": { "type": "string", "description": "The 6-digit TOTP code" } } } } } }, "responses": { "200": { "description": "Login successful", "content": { "application/json": { "schema": { "oneOf": [ { "type": "object", "required": [ "token" ], "properties": { "token": { "type": "string", "description": "Bearer authentication token (for users/employees)" } } }, { "type": "object", "required": [ "session" ], "properties": { "session": { "type": "string", "description": "Session token (for subusers)" } } } ] } } } }, "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", "g_recaptcha_response" ], "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" }, "g_recaptcha_response": { "type": "string", "description": "reCAPTCHA verification token" } } } } } }, "responses": { "201": { "description": "Customer registered successfully", "content": { "application/json": { "schema": {} } } }, "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", "g_recaptcha_response" ], "properties": { "customer_number": { "type": "integer", "description": "The customer number", "example": 123456 }, "g_recaptcha_response": { "type": "string", "description": "reCAPTCHA verification token" } } } } } }, "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", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/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", "g_recaptcha_response" ], "properties": { "token": { "type": "string", "description": "The password reset token" }, "password": { "type": "string", "description": "The new password" }, "g_recaptcha_response": { "type": "string", "description": "reCAPTCHA verification token" } } } } } }, "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", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/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" } } } } } } } } }, "/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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } }, "delete": { "tags": [ "Users" ], "summary": "Delete customer default department", "operationId": "deleteCustomerDefaultDepartment", "parameters": [ { "name": "customer_number", "in": "query", "schema": { "type": "integer" } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/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" }, "superuser_new_customer_email_notifications_enabled": { "type": "boolean" } } } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/user/permissions": { "get": { "tags": [ "Users" ], "summary": "Get user permissions", "operationId": "getUserPermissions", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/customers": { "get": { "tags": [ "Users" ], "summary": "List customers", "operationId": "listCustomers", "parameters": [ { "$ref": "#/components/parameters/PageParam" }, { "$ref": "#/components/parameters/PerPageParam" }, { "$ref": "#/components/parameters/SearchParam" }, { "name": "barred", "in": "query", "required": false, "description": "Optional e-conomic barred customer filter.", "schema": { "type": "string", "enum": [ "true", "false", "barred", "active", "1", "0" ] } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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": { "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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } }, "400": { "$ref": "#/components/responses/BadRequest" } } } }, "/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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } }, "delete": { "tags": [ "Departments" ], "summary": "Remove category from department", "operationId": "removeDepartmentCategory", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/department/gates": { "get": { "tags": [ "Departments" ], "summary": "List department gates", "description": "Retrieve department gates, optionally filtered by id", "operationId": "listDepartmentGates", "parameters": [ { "name": "id", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1 } }, { "$ref": "#/components/parameters/PageParam" }, { "$ref": "#/components/parameters/PerPageParam" }, { "$ref": "#/components/parameters/SearchParam" } ], "responses": { "200": { "description": "Department gates retrieved successfully", "content": { "application/json": { "schema": { "oneOf": [ { "$ref": "#/components/schemas/DepartmentGate" }, { "type": "array", "items": { "$ref": "#/components/schemas/DepartmentGate" } } ] } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "tags": [ "Departments" ], "summary": "Create department gate", "operationId": "createDepartmentGate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DepartmentGateCreate" } } } }, "responses": { "201": { "description": "Department gate created successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DepartmentGate" } } } } } }, "put": { "tags": [ "Departments" ], "summary": "Update department gate", "operationId": "updateDepartmentGate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DepartmentGateUpdate" } } } }, "responses": { "200": { "description": "Department gate updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DepartmentGate" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } }, "delete": { "tags": [ "Departments" ], "summary": "Delete department gate", "operationId": "deleteDepartmentGate", "parameters": [ { "name": "id", "in": "query", "required": true, "schema": { "type": "integer", "minimum": 1 } } ], "responses": { "200": { "description": "Department gate deleted", "content": { "application/json": { "schema": {} } } } } } }, "/department/relays": { "get": { "tags": [ "Departments" ], "summary": "List department relays", "description": "Retrieve department relays, optionally filtered by id", "operationId": "listDepartmentRelays", "parameters": [ { "name": "id", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1 } }, { "$ref": "#/components/parameters/PageParam" }, { "$ref": "#/components/parameters/PerPageParam" }, { "$ref": "#/components/parameters/SearchParam" } ], "responses": { "200": { "description": "Department relays retrieved successfully", "content": { "application/json": { "schema": { "oneOf": [ { "$ref": "#/components/schemas/DepartmentRelay" }, { "type": "array", "items": { "$ref": "#/components/schemas/DepartmentRelay" } } ] } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "tags": [ "Departments" ], "summary": "Create department relay", "operationId": "createDepartmentRelay", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DepartmentRelayCreate" } } } }, "responses": { "201": { "description": "Department relay created successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DepartmentRelay" } } } } } }, "put": { "tags": [ "Departments" ], "summary": "Update department relay", "operationId": "updateDepartmentRelay", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DepartmentRelayUpdate" } } } }, "responses": { "200": { "description": "Department relay updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DepartmentRelay" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } }, "delete": { "tags": [ "Departments" ], "summary": "Delete department relay", "operationId": "deleteDepartmentRelay", "parameters": [ { "name": "id", "in": "query", "required": true, "schema": { "type": "integer", "minimum": 1 } } ], "responses": { "200": { "description": "Department relay deleted", "content": { "application/json": { "schema": {} } } } } } }, "/department/lanes/dynamic-image": { "get": { "tags": [ "Departments" ], "summary": "Generate dynamic image for a department lane", "description": "Returns a composed machine UI image for the specified department lane.\nYou can optionally highlight button indices, set the current step indicator, and toggle only-current-step mode.\n", "operationId": "getDepartmentLaneDynamicImage", "parameters": [ { "name": "department", "in": "query", "required": true, "description": "Department ID", "schema": { "type": "integer", "minimum": 1 } }, { "name": "lane", "in": "query", "required": true, "description": "Lane ID", "schema": { "type": "integer", "minimum": 1 } }, { "name": "buttons", "in": "query", "required": false, "description": "Highlighted button IDs (0-indexed). Accepts CSV, JSON array, or repeated query params.", "schema": { "oneOf": [ { "type": "string" }, { "type": "array", "items": { "type": "integer" } } ] } }, { "name": "current_step", "in": "query", "required": false, "description": "Current step indicator (non-negative integer)", "schema": { "type": "integer", "minimum": 0 } }, { "name": "only_current_step", "in": "query", "required": false, "description": "If true, only draw the current step highlight", "schema": { "type": "boolean" } }, { "name": "vehicle_type", "in": "query", "required": false, "description": "Vehicle type selection override (nullable non-negative integer)", "schema": { "type": "integer", "minimum": 0 } } ], "responses": { "200": { "description": "Dynamic image rendered successfully", "content": { "image/png": { "schema": { "type": "string", "format": "binary" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "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", "content": { "application/json": { "schema": {} } } }, "400": { "$ref": "#/components/responses/BadRequest" } } } }, "/guest/departments": { "get": { "tags": [ "Departments" ], "summary": "List public departments", "description": "Get list of departments without authentication", "operationId": "listGuestDepartments", "security": [], "parameters": [ { "name": "include_lanes", "in": "query", "description": "Whether to include lane status and self-serve information", "schema": { "type": "boolean" } } ], "responses": { "200": { "description": "Departments retrieved successfully", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/DepartmentGuest" } } } } } } } }, "/department/selfserve/machine-types": { "get": { "tags": [ "Self-Serve" ], "summary": "List reusable self-serve machine types", "operationId": "listSelfserveMachineTypes", "parameters": [ { "name": "id", "in": "query", "required": false, "schema": { "type": "integer" } }, { "$ref": "#/components/parameters/PageParam" }, { "$ref": "#/components/parameters/PerPageParam" }, { "$ref": "#/components/parameters/SearchParam" } ], "responses": { "200": { "description": "Successfully retrieved machine types", "content": { "application/json": { "schema": { "oneOf": [ { "$ref": "#/components/schemas/SelfserveMachineType" }, { "type": "array", "items": { "$ref": "#/components/schemas/SelfserveMachineType" } } ] } } } }, "404": { "$ref": "#/components/responses/NotFound" } } }, "post": { "tags": [ "Self-Serve" ], "summary": "Add reusable self-serve machine type", "operationId": "addSelfserveMachineType", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "name" ], "properties": { "name": { "type": "string" }, "description": { "type": "string", "nullable": true } } } } } }, "responses": { "200": { "description": "Successfully added machine type", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SelfserveMachineType" } } } } } }, "put": { "tags": [ "Self-Serve" ], "summary": "Update reusable self-serve machine type", "operationId": "updateSelfserveMachineType", "parameters": [ { "name": "id", "in": "query", "required": true, "schema": { "type": "integer" } } ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string" }, "description": { "type": "string", "nullable": true } } } } } }, "responses": { "200": { "description": "Successfully updated machine type", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SelfserveMachineType" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } }, "delete": { "tags": [ "Self-Serve" ], "summary": "Delete reusable self-serve machine type", "operationId": "deleteSelfserveMachineType", "parameters": [ { "name": "id", "in": "query", "required": true, "schema": { "type": "integer" } } ], "responses": { "200": { "description": "Successfully deleted machine type", "content": { "application/json": { "schema": { "type": "string", "example": "Machine type deleted" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/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. Questions are typically shared across departments and lanes by omitting department, lane, and product, which default to 0.", "operationId": "addSelfserveQuestion", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "question", "description" ], "properties": { "department": { "type": "integer", "default": 0 }, "lane": { "type": "integer", "default": 0 }, "product": { "type": "integer", "default": 0 }, "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" } }, { "name": "machine_type_id", "in": "query", "description": "Filter by reusable machine type ID", "schema": { "type": "integer" } }, { "name": "machine_type_id", "in": "query", "description": "Filter by reusable machine type 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. Either provide a reusable machine_type_id or a legacy department/lane/product scope.", "operationId": "addSelfserveCondition", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "name", "description" ], "properties": { "department": { "type": "integer", "default": 0 }, "lane": { "type": "integer", "default": 0 }, "product": { "type": "integer", "default": 0 }, "machine_type_id": { "type": "integer", "nullable": true }, "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" }, "machine_type_id": { "type": "integer", "nullable": true }, "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/DepartmentSelfserveVehicleConditionMutationResponse" } } } }, "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/DepartmentSelfserveVehicleConditionMutationResponse" } } } }, "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": "object", "properties": { "message": { "type": "string", "example": "Condition deleted" }, "selfserve": { "allOf": [ { "$ref": "#/components/schemas/SelfserveWashSummary" } ], "nullable": true } } } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/department/selfserve/vehicle/allowed": { "get": { "tags": [ "Self-Serve" ], "summary": "Check whether self-serve is allowed for a vehicle on a lane", "operationId": "getSelfserveVehicleAllowed", "parameters": [ { "name": "lane_id", "in": "query", "required": true, "schema": { "type": "integer" } }, { "name": "reg", "in": "query", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Successfully evaluated self-serve eligibility", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SelfserveVehicleAllowedResponse" } } } }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/department/selfserve/washes/summary": { "get": { "tags": [ "Self-Serve" ], "summary": "Get self-serve wash summary", "operationId": "getSelfserveWashSummary", "parameters": [ { "name": "session_id", "in": "query", "required": false, "schema": { "type": "integer" } }, { "name": "lane_id", "in": "query", "required": false, "schema": { "type": "integer" } }, { "name": "reg", "in": "query", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Successfully retrieved self-serve wash summary", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SelfserveWashSummary" } } } }, "403": { "$ref": "#/components/responses/Forbidden" }, "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. Either provide a reusable machine_type_id or a legacy department/lane/product scope.", "operationId": "addSelfserveTask", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "task", "description" ], "properties": { "department": { "type": "integer", "default": 0 }, "lane": { "type": "integer", "default": 0 }, "product": { "type": "integer", "default": 0 }, "machine_type_id": { "type": "integer", "nullable": true }, "condition_id": { "type": "integer", "nullable": true }, "task": { "type": "string" }, "description": { "type": "string" }, "order_priority": { "type": "integer", "default": 0 }, "services": { "type": "array", "description": "Optional services enabled by this task. Items must be valid service enum names.", "items": { "$ref": "#/components/schemas/SelfserveLaneService" } }, "buttons": { "type": "array", "description": "Optional dynamic image button IDs enabled by this task.", "items": { "type": "integer" }, "default": [] }, "dynamic_images_vehicle_type": { "type": "integer", "nullable": true, "description": "Optional vehicle type selection override for the machine UI. Integer >= 0 or null." } } } } } }, "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" }, "machine_type_id": { "type": "integer", "nullable": true }, "condition_id": { "type": "integer", "nullable": true }, "task": { "type": "string" }, "description": { "type": "string" }, "order_priority": { "type": "integer" }, "services": { "type": "array", "nullable": true, "description": "Services enabled by this task. Set to null to clear all services.", "items": { "$ref": "#/components/schemas/SelfserveLaneService" } }, "buttons": { "type": "array", "nullable": true, "description": "Button IDs enabled by this task. Set to null to clear all buttons.", "items": { "type": "integer" } }, "dynamic_images_vehicle_type": { "type": "integer", "nullable": true, "description": "Vehicle type selection override. Set to null to clear." } } } } } }, "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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } }, "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": { "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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } }, "400": { "$ref": "#/components/responses/BadRequest" } } } }, "/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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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.\n\nAccess control:\n- A user may only access goals where the goal's `departments` set is a subset of the user's departments.\n- Users with the `superuser` permission may access all goals.\n", "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.\n\nAccess control:\n- The provided `departments` must be a subset of the user's departments unless the user has `superuser`.\n", "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`.\n\nAccess control:\n- The creator (`created_by`) may update regardless of department membership.\n- 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`.\n", "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" } } } }, "/goals/department/progress-alert/test": { "post": { "tags": [ "Goals" ], "summary": "Send a test progress alert for a department goal", "description": "Sends a progress alert for a department goal to the destination defined in the goal's criteria.\n\nPermission required: `goals_department_progress_alert_test`.\n\nAccess control:\n- The caller must be a superuser or belong to all departments targeted by the goal.\n\nBehavior:\n- Looks up the goal by `id`.\n- Rebuilds the criteria from stored JSON and attaches the goal's departments.\n- Renders the alert using the server-side renderer (respecting progress type/style/format and destination limits).\n- Sends the alert to Slack, Email, or SMS depending on `progress_alert_destination`, unless overridden.\n", "operationId": "sendDepartmentGoalProgressAlertTest", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "id" ], "properties": { "id": { "type": "integer", "description": "The department goal id", "example": 42 }, "overrideDestination": { "type": "string", "description": "Override the destination for this test", "enum": [ "SLACK", "EMAIL", "SMS", "NONE" ], "example": "SLACK" }, "email_to": { "type": "string", "format": "email", "description": "Email recipient when destination is EMAIL", "example": "tester@example.com" }, "subject": { "type": "string", "description": "Optional email subject when destination is EMAIL", "example": "Dept Goal Progress Test" }, "sms_to": { "description": "One or more MSISDN recipients when destination is SMS", "oneOf": [ { "type": "string", "description": "Comma or semicolon separated list", "example": "+4512345678, +4598765432" }, { "type": "array", "items": { "type": "string" }, "example": [ "+4512345678", "+4598765432" ] } ] }, "slack_webhook": { "type": "string", "description": "Slack webhook URL when destination is SLACK", "example": "https://hooks.slack.com/services/T000/B000/XXX" }, "department_id": { "type": "integer", "description": "Department id to use that department's Slack webhook when destination is SLACK", "example": 3 } } } } } }, "responses": { "200": { "description": "Alert sent successfully", "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "Goal id" }, "destination": { "type": "string", "description": "Final destination used", "enum": [ "SLACK", "EMAIL", "SMS", "NONE" ] }, "target": { "description": "The target used for delivery (email address, phone numbers, department id, or webhook)" }, "message_preview": { "type": "string", "description": "Rendered message preview" }, "provider_response": { "description": "Provider-specific response or status message" } } } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "500": { "$ref": "#/components/responses/InternalServerError" } } }, "delete": { "tags": [ "Goals" ], "summary": "Delete department goal", "description": "Delete a department goal by `id`.\n\nAccess control:\n- The creator (`created_by`) may delete regardless of department membership.\n- Otherwise the user must satisfy the subset rule or have `superuser`.\n", "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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/admin/bookings/sync": { "post": { "tags": [ "Bookings" ], "summary": "Sync booking from external system", "operationId": "syncBooking", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/superuser/bookings/sync/all": { "post": { "tags": [ "Bookings" ], "summary": "Sync all bookings from external system", "operationId": "syncAllBookings", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } }, "post": { "tags": [ "Invoices" ], "summary": "Create collected invoice", "description": "Create a new collected invoice", "operationId": "createCollectedInvoice", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "201": { "description": "Collected invoice created successfully", "content": { "application/json": { "schema": {} } } } } }, "put": { "tags": [ "Invoices" ], "summary": "Update collected invoice", "description": "Update a collected invoice", "operationId": "updateCollectedInvoice", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Collected invoice updated successfully", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/collected-invoices/economic/compare": { "get": { "tags": [ "Invoices" ], "summary": "Compare collected invoice totals with E-conomic", "description": "Compares a collected invoice in the system with its corresponding invoice in E-conomic.\nReturns totals from both sources, their difference, and any warnings detected during comparison.\n", "operationId": "compareCollectedInvoiceEconomic", "parameters": [ { "name": "collected_invoice_id", "in": "query", "required": true, "description": "The internal collected invoice ID to compare", "schema": { "type": "integer", "minimum": 1 } } ], "responses": { "200": { "description": "Comparison completed successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectedInvoiceEconomicCompareResponse" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/collected-invoices/economic/v2/details": { "get": { "tags": [ "Invoices" ], "summary": "Get deep V2 e-conomic invoice details", "description": "Returns normalized internal lines and best-effort fetched draft/booked e-conomic lines\nfor a collected invoice, including department distributions and warnings.\n", "operationId": "getCollectedInvoiceEconomicV2Details", "parameters": [ { "name": "collected_invoice_id", "in": "query", "required": true, "schema": { "type": "integer", "minimum": 1 } } ], "responses": { "200": { "description": "Details resolved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectedInvoiceEconomicV2DetailsResponse" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/collected-invoices/economic/v2/compare": { "get": { "tags": [ "Invoices" ], "summary": "Compare internal invoice with draft/booked (V2)", "operationId": "compareCollectedInvoiceEconomicV2", "parameters": [ { "name": "collected_invoice_id", "in": "query", "required": true, "schema": { "type": "integer", "minimum": 1 } } ], "responses": { "200": { "description": "Comparison completed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectedInvoiceEconomicV2CompareResponse" }, "examples": { "exactMatch": { "summary": "Exact match between internal and draft/booked", "value": { "collected_invoice_id": 123, "warnings": [], "comparison": { "totals": { "internal_net_total": 694 }, "targets": { "draft": { "target": "draft", "status": "exact_match", "overall_match": true }, "booked": { "target": "booked", "status": "exact_match", "overall_match": true } } } } }, "partialMismatch": { "summary": "Partial mismatch with line and department differences", "value": { "collected_invoice_id": 123, "warnings": [ "Non-billable line count differs" ], "comparison": { "totals": { "internal_net_total": 694 }, "targets": { "draft": { "target": "draft", "status": "partial_mismatch", "overall_match": false, "mismatch_reasons": [ "quantity_mismatch", "department_total_mismatch" ] } } } } }, "missingBooked": { "summary": "Missing booked target", "value": { "collected_invoice_id": 123, "comparison": { "totals": { "internal_net_total": 694 }, "targets": { "booked": { "target": "booked", "status": "missing_target", "overall_match": false } } } } } } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/collected-invoices/economic/v2/compare/bulk": { "post": { "tags": [ "Invoices" ], "summary": "Bulk compare collected invoices against draft/booked (V2)", "operationId": "compareCollectedInvoiceEconomicV2Bulk", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "collected_invoice_ids" ], "properties": { "collected_invoice_ids": { "type": "array", "minItems": 1, "maxItems": 200, "items": { "type": "integer", "minimum": 1 } } } } } } }, "responses": { "200": { "description": "Bulk comparison completed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectedInvoiceEconomicV2CompareBulkResponse" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/collected-invoices/economic/v2/revenue-statistics": { "get": { "tags": [ "Invoices" ], "summary": "Get overall booked revenue statistics from e-conomic (V2)", "description": "Aggregates booked e-conomic revenue across invoices and lines, with optional filters\nfor date range, customer(s), department(s), currency, and barred-customer status.\n", "operationId": "getCollectedInvoiceEconomicV2RevenueStatistics", "parameters": [ { "name": "dateFrom", "in": "query", "required": false, "description": "Start date (inclusive), defaults to first day of current month.", "schema": { "type": "string", "format": "date" } }, { "name": "dateTo", "in": "query", "required": false, "description": "End date (inclusive), defaults to today.", "schema": { "type": "string", "format": "date" } }, { "name": "customer_numbers", "in": "query", "required": false, "description": "Comma-separated customer numbers to include.", "schema": { "type": "string", "example": "42493959,42493960" } }, { "name": "department_numbers", "in": "query", "required": false, "description": "Comma-separated department numbers to include.", "schema": { "type": "string", "example": "75,10" } }, { "name": "currency", "in": "query", "required": false, "description": "Restrict to a specific invoice currency.", "schema": { "type": "string", "example": "DKK" } }, { "name": "barred", "in": "query", "required": false, "description": "Filter by e-conomic customer barred status.", "schema": { "type": "string", "enum": [ "all", "barred", "active" ], "default": "all" } }, { "name": "max_pages", "in": "query", "required": false, "description": "Safety cap for paginated e-conomic reads.", "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 10 } } ], "responses": { "200": { "description": "Revenue statistics resolved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectedInvoiceEconomicV2RevenueStatisticsResponse" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionResponse" } } } } } } }, "/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", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoicingWashSubscriptionsDistributionResponse" } } } } } } }, "/superuser/invoicing/period/distribution/v2/all": { "get": { "tags": [ "Invoices" ], "summary": "Get version-aware historical distribution (all)", "operationId": "getInvoicingPeriodDistributionV2All", "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": "Version-aware historical distribution (all categories)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoicingDistributionV2AllResponse" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/superuser/invoicing/period/distribution/v2/fixed-pricing": { "get": { "tags": [ "Invoices" ], "summary": "Get version-aware historical fixed pricing distribution", "operationId": "getInvoicingPeriodDistributionV2FixedPricing", "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": "Version-aware fixed pricing distribution", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoicingDistributionV2FixedPricingResponse" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/superuser/invoicing/period/distribution/v2/wash-subscriptions": { "get": { "tags": [ "Invoices" ], "summary": "Get version-aware historical wash subscription distribution", "operationId": "getInvoicingPeriodDistributionV2WashSubscriptions", "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": "Version-aware wash subscription distribution", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoicingDistributionV2WashSubscriptionsResponse" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/superuser/invoicing/period/distribution/v2/customer-prices": { "get": { "tags": [ "Invoices" ], "summary": "Get version-aware historical customer-price discount distribution", "operationId": "getInvoicingPeriodDistributionV2CustomerPrices", "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": "Version-aware customer-price discount distribution", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoicingDistributionV2CustomerPricesResponse" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/superuser/invoicing/period/distribution/v2/booked-department-75": { "get": { "tags": [ "Invoices" ], "summary": "Get booked e-conomic department 75 redistribution", "operationId": "getInvoicingPeriodDistributionV2BookedDepartment75", "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": "Actual booked e-conomic department 75 net amounts redistributed to internal departments", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoicingDistributionV2BookedDepartment75Response" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/superuser/customers/pricing-history": { "get": { "tags": [ "Invoices" ], "summary": "Get customer versioned pricing/subscription/discount timeline", "operationId": "getCustomerPricingHistoryV2", "parameters": [ { "name": "customer_number", "in": "query", "required": true, "schema": { "type": "integer", "minimum": 1 } }, { "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": "Customer timeline resolved", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomerPricingHistoryResponse" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "500": { "$ref": "#/components/responses/InternalServerError" } } } }, "/vehicles": { "get": { "tags": [ "Vehicles" ], "summary": "List vehicles", "description": "List vehicles or fetch a specific vehicle when `id` is provided.\n\n- When `id` is present, returns a single vehicle object (404 if not found).\n- Otherwise returns a paginated list of vehicles.\n\nPermissions:\n- Own scope: `list_own_vehicles` (linked to subuser node `VEHICLES_LIST`).\n- Broader scope: `list_vehicles_other`.\n\nSubusers may specify header `X-Customer-Number` to target a specific customer. If the broader\npermission is missing, the list will automatically be restricted to the effective customer context.\n", "operationId": "listVehicles", "parameters": [ { "$ref": "#/components/parameters/PageParam" }, { "$ref": "#/components/parameters/PerPageParam" }, { "$ref": "#/components/parameters/XCustomerNumber" }, { "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": "Vehicle(s) retrieved successfully", "content": { "application/json": { "schema": { "oneOf": [ { "$ref": "#/components/schemas/Vehicle" }, { "type": "array", "items": { "$ref": "#/components/schemas/Vehicle" } } ] } } } }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" } } }, "post": { "tags": [ "Vehicles" ], "summary": "Add vehicle", "operationId": "addVehicle", "description": "Create a new vehicle for a customer.\n\nPermissions:\n- Own scope: `add_vehicle` (linked to subuser node `VEHICLES_ADD`).\n- Broader scope: `add_vehicle_other`.\n", "parameters": [ { "$ref": "#/components/parameters/XCustomerNumber" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "reg", "type", "wash_subscription" ], "properties": { "reg": { "type": "string", "minLength": 2, "maxLength": 12, "description": "Vehicle registration number" }, "type": { "type": "integer", "description": "Product ID representing the vehicle wash type" }, "wash_subscription": { "type": "boolean" }, "reference": { "type": "string", "maxLength": 255, "nullable": true }, "customer_id": { "type": "integer", "description": "Optional explicit target customer. Defaults to the effective customer context." } } } } } }, "responses": { "200": { "description": "Vehicle created", "content": { "application/json": { "schema": {} } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "403": { "$ref": "#/components/responses/Forbidden" } } }, "put": { "tags": [ "Vehicles" ], "summary": "Edit vehicle", "operationId": "editVehicle", "description": "Update fields on an existing vehicle.\n\nPermissions:\n- Own scope: `edit_vehicle` (linked to subuser node `VEHICLES_EDIT`).\n- Broader scope: `edit_vehicle_other`.\n", "parameters": [ { "$ref": "#/components/parameters/XCustomerNumber" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "id" ], "properties": { "id": { "type": "integer" }, "reg": { "type": "string", "minLength": 2, "maxLength": 12 }, "type": { "type": "integer" }, "wash_subscription": { "type": "boolean" }, "reference": { "type": "string", "maxLength": 255, "nullable": true } } } } } }, "responses": { "200": { "description": "Vehicle updated", "content": { "application/json": { "schema": {} } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" } } }, "delete": { "tags": [ "Vehicles" ], "summary": "Delete vehicle", "operationId": "deleteVehicle", "description": "Delete an existing vehicle.\n\nPermissions:\n- Own scope: `delete_vehicle` (linked to subuser node `VEHICLES_DELETE`).\n- Broader scope: `delete_vehicle_other`.\n", "parameters": [ { "$ref": "#/components/parameters/XCustomerNumber" }, { "name": "id", "in": "query", "required": true, "schema": { "type": "integer" } } ], "responses": { "200": { "description": "Vehicle deleted", "content": { "application/json": { "schema": {} } } }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/vehicles/addons/available": { "get": { "tags": [ "Vehicles" ], "summary": "Get available vehicle addons", "description": "Get list of available addons for a vehicle.\n\nPermissions:\n- Own scope: `list_vehicle_addon_own` (linked to subuser node `VEHICLES_LIST`).\n- Broader scope: `list_vehicles_addon_other`.\n", "operationId": "getAvailableVehicleAddons", "parameters": [ { "$ref": "#/components/parameters/XCustomerNumber" }, { "name": "id", "in": "query", "required": true, "schema": { "type": "integer" } } ], "responses": { "200": { "description": "Available addons retrieved successfully", "content": { "application/json": { "schema": {} } } }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/vehicles/addons/toggle": { "post": { "tags": [ "Vehicles" ], "summary": "Toggle vehicle addon", "description": "Enable or disable a vehicle addon for a vehicle.\n\nPermissions:\n- Own scope: `toggle_vehicle_addon_own` (linked to subuser node `VEHICLES_EDIT`).\n- Broader scope: `toggle_vehicle_addon_other`.\n", "operationId": "toggleVehicleAddon", "parameters": [ { "$ref": "#/components/parameters/XCustomerNumber" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "vehicle_id", "addon_id" ], "properties": { "vehicle_id": { "type": "integer" }, "addon_id": { "type": "integer" } } } } } }, "responses": { "200": { "description": "Vehicle addon toggled successfully", "content": { "application/json": { "schema": {} } } }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/department/vehicles/unknown-customer": { "get": { "tags": [ "Vehicles" ], "summary": "Get unknown customer vehicles in department", "operationId": "getUnknownCustomerVehicles", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/vehicles/set-auto-start-on-lpr": { "post": { "tags": [ "Vehicles" ], "summary": "Set auto start on LPR", "operationId": "setVehicleAutoStartOnLpr", "description": "Enable or disable automatic start on LPR for a vehicle in XL Vask.\n\nPermissions:\n- Own scope: `set_auto_start_on_lpr` (linked to subuser node `VEHICLES_EDIT`).\n- Broader scope: `set_auto_start_on_lpr_other`.\n", "parameters": [ { "$ref": "#/components/parameters/XCustomerNumber" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "active" ], "properties": { "id": { "type": "integer" }, "active": { "type": "boolean" } } } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/vehicles/set-vehicle-type-id": { "post": { "tags": [ "Vehicles" ], "summary": "Set vehicle type ID", "operationId": "setVehicleTypeId", "description": "Set or change the XL Vask `vehicleTypeId` for a vehicle.\n\nPermissions:\n- Own scope: `set_vehicle_type_id` (linked to subuser node `VEHICLES_EDIT`).\n- Broader scope: `set_vehicle_type_id_other`.\n", "parameters": [ { "$ref": "#/components/parameters/XCustomerNumber" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "vehicleTypeId" ], "properties": { "id": { "type": "integer" }, "vehicleTypeId": { "type": "string", "minLength": 1, "maxLength": 50 } } } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/superuser/users-with-vehicle-subscriptions": { "get": { "tags": [ "Vehicles" ], "summary": "Get users with vehicle subscriptions", "operationId": "getUsersWithVehicleSubscriptions", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/vehicles/search": { "get": { "tags": [ "Vehicles" ], "summary": "Search vehicles", "operationId": "searchVehicles", "parameters": [ { "name": "search", "in": "query", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } }, "delete": { "tags": [ "Orders" ], "summary": "Delete Stripe payment intent", "operationId": "deleteStripePaymentIntent", "parameters": [ { "name": "id", "in": "query", "required": true, "schema": { "type": "integer" } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/statistics/income/departments": { "get": { "tags": [ "Statistics" ], "summary": "Get total income today by departments", "operationId": "getTotalIncomeTodayByDepartments", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/statistics/economic/totals": { "get": { "tags": [ "Statistics" ], "summary": "Get total economic statistics", "operationId": "getEconomicTotals", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/statistics/economic/totals/department_sent_invoice_totals": { "get": { "tags": [ "Statistics" ], "summary": "Get department sent invoice totals", "operationId": "getDepartmentSentInvoiceTotals", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/statistics/economic/totals/department_draft_invoice_totals": { "get": { "tags": [ "Statistics" ], "summary": "Get department draft invoice totals", "operationId": "getDepartmentDraftInvoiceTotals", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } }, "403": { "$ref": "#/components/responses/Forbidden" } } } }, "/worker/debug/on": { "get": { "tags": [ "Worker" ], "summary": "Enable worker debug", "operationId": "enableWorkerDebug", "responses": { "200": { "description": "Worker debug enabled", "content": { "application/json": { "schema": {} } } }, "403": { "$ref": "#/components/responses/Forbidden" } } } }, "/worker/debug/off": { "get": { "tags": [ "Worker" ], "summary": "Disable worker debug", "operationId": "disableWorkerDebug", "responses": { "200": { "description": "Worker debug disabled", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/numberplatescans/post": { "get": { "tags": [ "Plate Scans" ], "summary": "Get post-scan results", "operationId": "getPlateScanPostResults", "responses": { "200": { "description": "Post-scan results retrieved successfully", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/relay/button/press/post": { "get": { "tags": [ "Plate Scans" ], "summary": "Record machine start button press webhook", "operationId": "addButtonPress", "parameters": [ { "name": "token", "in": "query", "required": false, "schema": { "type": "string" } }, { "name": "lane_id", "in": "query", "required": false, "schema": { "type": "integer" } }, { "name": "reg", "in": "query", "required": false, "schema": { "type": "string" } } ], "responses": { "201": { "description": "Button press recorded and linked to a self-serve wash session", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MachineButtonPressWebhookResponse" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } }, "post": { "tags": [ "Plate Scans" ], "summary": "Record machine start button press webhook", "operationId": "addButtonPressPost", "requestBody": { "required": false, "content": { "application/json": { "schema": { "type": "object", "properties": { "token": { "type": "string" }, "lane_id": { "type": "integer" }, "reg": { "type": "string" } } } } } }, "responses": { "201": { "description": "Button press recorded and linked to a self-serve wash session", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MachineButtonPressWebhookResponse" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/economic/customers/import": { "post": { "tags": [ "Modules" ], "summary": "Import e-conomic customers", "description": "Import customers from e-conomic", "operationId": "importEconomicCustomers", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Customers imported successfully", "content": { "application/json": { "schema": {} } } } } } }, "/economic/departments": { "get": { "tags": [ "Modules" ], "summary": "Get e-conomic departments", "operationId": "getEconomicDepartments", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/economic/products": { "get": { "tags": [ "Modules" ], "summary": "Get e-conomic products", "operationId": "getEconomicProducts", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/economic/invoice/draft/export": { "post": { "tags": [ "Modules" ], "summary": "Export draft invoice to e-conomic", "description": "Export a draft invoice to e-conomic", "operationId": "exportDraftInvoiceToEconomic", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Draft invoice exported successfully", "content": { "application/json": { "schema": {} } } } } } }, "/economic/invoice/export": { "post": { "tags": [ "Modules" ], "summary": "Export invoice to e-conomic", "description": "Export a booked invoice to e-conomic", "operationId": "exportInvoiceToEconomic", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Invoice exported successfully", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/modules/stripe/invoice": { "post": { "tags": [ "Modules" ], "summary": "Create Stripe invoice", "description": "Create an invoice in Stripe", "operationId": "createStripeInvoice", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "201": { "description": "Stripe invoice created successfully", "content": { "application/json": { "schema": {} } } } } } }, "/modules/stripe/terminal/readers": { "get": { "tags": [ "Modules" ], "summary": "List Stripe terminal readers", "operationId": "listStripeTerminalReaders", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/modules/stripe/terminal/locations": { "get": { "tags": [ "Modules" ], "summary": "List Stripe terminal locations", "operationId": "listStripeTerminalLocations", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/modules/backup/backups": { "get": { "tags": [ "Modules" ], "summary": "List backup modules", "operationId": "listBackupModules", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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" } } } }, "/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" } } } } } } }, "/modules/self-serve/lane/services/allowed": { "post": { "tags": [ "Modules" ], "summary": "Set allowed services for a lane based on shown tasks", "description": "Updates the set of services that are allowed to be manually activated for a given self-serve lane,\nderived from the tasks currently shown to the user after answering the self-serve questions.\nThis endpoint does not activate anything by itself; it only sets what is allowed to be activated.\n", "operationId": "setSelfServeLaneAllowedServices", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "lane_id" ], "properties": { "lane_id": { "type": "integer" }, "task_ids": { "type": "array", "description": "List of task IDs that are currently shown to the user", "items": { "type": "integer" } } } } } } }, "responses": { "200": { "description": "Allowed services updated", "content": { "application/json": { "schema": { "type": "object", "properties": { "lane_id": { "type": "integer" }, "allowed_services": { "type": "array", "items": { "type": "string" } } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" } } } }, "/modules/self-serve/lane/relay/machine/enable": { "post": { "tags": [ "Modules" ], "summary": "Manually enable MACHINE relay for a lane", "description": "Manually turns on the MACHINE relay for a self-serve lane if and only if the current allowed services\ninclude `MACHINE` (set via `/modules/self-serve/lane/services/allowed`). The relay is never automatically\nenabled; an explicit call to this endpoint is required.\n", "operationId": "enableSelfServeLaneMachineRelay", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "lane_id" ], "properties": { "lane_id": { "type": "integer" }, "duration": { "type": "integer", "description": "Optional number of seconds after which the relay should automatically turn off" } } } } } }, "responses": { "200": { "description": "MACHINE relay enabled", "content": { "application/json": { "schema": { "type": "object", "properties": { "lane_id": { "type": "integer" }, "relay": { "type": "string" }, "enabled": { "type": "boolean" }, "duration": { "type": "integer", "nullable": true } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "description": "Not allowed to enable MACHINE relay (no matching task currently shown)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/modules/self-serve/lane/force/machine/enable": { "post": { "tags": [ "Modules" ], "summary": "Force enable MACHINE relay and mark lane as in-wash (superusers only)", "description": "Superuser/emergency endpoint. Bypasses the allowed services gating and directly turns on the MACHINE relay.\nAlso ensures the lane is marked as OCCUPIED and IN_WASH with a wash start timestamp if not already set.\n", "operationId": "forceEnableSelfServeLaneMachine", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "lane_id" ], "properties": { "lane_id": { "type": "integer" }, "duration": { "type": "integer", "nullable": true, "description": "Optional number of seconds after which the relay should automatically turn off" }, "license_plate": { "type": "string", "nullable": true, "description": "Optional license plate to associate with the lane" } } } } } }, "responses": { "200": { "description": "MACHINE relay force-enabled and lane marked in-wash", "content": { "application/json": { "schema": { "type": "object", "properties": { "lane_id": { "type": "integer" }, "forced": { "type": "boolean" }, "machine": { "type": "string", "enum": [ "ENABLED" ] }, "duration": { "type": "integer", "nullable": true }, "status": { "type": "string" }, "state": { "type": "string" }, "wash_start_time": { "type": "integer" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" } } } }, "/modules/self-serve/lane/force/machine/disable": { "post": { "tags": [ "Modules" ], "summary": "Force disable MACHINE relay but keep lane as in-wash (superusers only)", "description": "Superuser/emergency endpoint. Turns off the MACHINE relay while ensuring the lane remains in an IN_WASH state\n(simulating a started wash without machine assistance).\n", "operationId": "forceDisableSelfServeLaneMachine", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "lane_id" ], "properties": { "lane_id": { "type": "integer" }, "license_plate": { "type": "string", "nullable": true } } } } } }, "responses": { "200": { "description": "MACHINE relay force-disabled and lane ensured in-wash", "content": { "application/json": { "schema": { "type": "object", "properties": { "lane_id": { "type": "integer" }, "forced": { "type": "boolean" }, "machine": { "type": "string", "enum": [ "DISABLED" ] }, "status": { "type": "string" }, "state": { "type": "string" }, "wash_start_time": { "type": "integer" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/modules/weatherapi/current": { "get": { "tags": [ "Modules" ], "summary": "Get current weather", "description": "Get current weather data from WeatherAPI for a location query", "operationId": "weatherApiCurrent", "parameters": [ { "name": "q", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Location query (e.g. city, postal code, or latitude,longitude)" } ], "responses": { "200": { "description": "Current weather retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WeatherApiObjectResponse" } } } } } } }, "/modules/weatherapi/forecast": { "get": { "tags": [ "Modules" ], "summary": "Get weather forecast", "description": "Get forecast weather data from WeatherAPI", "operationId": "weatherApiForecast", "parameters": [ { "name": "q", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Location query (e.g. city, postal code, or latitude,longitude)" }, { "name": "days", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "maximum": 14 }, "description": "Number of forecast days" } ], "responses": { "200": { "description": "Forecast weather retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WeatherApiObjectResponse" } } } } } } }, "/modules/weatherapi/search": { "get": { "tags": [ "Modules" ], "summary": "Search weather locations", "description": "Search location suggestions from WeatherAPI", "operationId": "weatherApiSearch", "parameters": [ { "name": "q", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Search text" } ], "responses": { "200": { "description": "Location search results retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WeatherApiObjectResponse" } } } } } } }, "/departments/weather": { "get": { "tags": [ "Departments" ], "summary": "Get department weather timeline", "description": "Returns hourly weather, washes, hours and productivity status for a department", "operationId": "getDepartmentWeatherTimeline", "parameters": [ { "name": "id", "in": "query", "required": true, "schema": { "type": "integer", "minimum": 1 }, "description": "Department ID" } ], "responses": { "200": { "description": "Department weather timeline retrieved successfully", "content": { "application/json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/SuccessResponse" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/DepartmentWeatherTimelineEntry" } } } } ], "$ref": "#/components/schemas/DepartmentWeatherTimelineResponse" } } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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" } } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } }, "post": { "tags": [ "Forms" ], "summary": "Submit form", "description": "Submit a form", "operationId": "submitForm", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "data" ], "properties": { "id": { "type": "string", "description": "Form identifier" }, "data": { "type": "object", "description": "Form submission data" }, "g_recaptcha_response": { "type": "string", "description": "reCAPTCHA verification token (required if not authenticated)" } } } } } }, "responses": { "201": { "description": "Form submitted successfully", "content": { "application/json": { "schema": {} } } } } } }, "/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/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", "content": { "application/json": { "schema": {} } } } } }, "post": { "tags": [ "Users" ], "summary": "Add customer attribute", "description": "Add a custom attribute to a customer", "operationId": "addCustomerAttribute", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "201": { "description": "Customer attribute added successfully", "content": { "application/json": { "schema": {} } } } } }, "delete": { "tags": [ "Users" ], "summary": "Delete customer attribute", "description": "Remove a custom attribute from a customer", "operationId": "deleteCustomerAttribute", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Customer attribute deleted successfully", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } }, "post": { "tags": [ "Users" ], "summary": "Add customer note", "description": "Add a note to a customer", "operationId": "addCustomerNote", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "201": { "description": "Customer note added successfully", "content": { "application/json": { "schema": {} } } } } }, "delete": { "tags": [ "Users" ], "summary": "Delete customer note", "description": "Remove a note from a customer", "operationId": "deleteCustomerNote", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Customer note deleted successfully", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/search/system": { "get": { "tags": [ "Search" ], "summary": "System-wide search", "description": "Search across all supported entities with permission-aware filtering and optional intent parsing debug metadata. Customer records and customer-related entities are matched against a local e-conomic customer index (name/address/email/CVR) that is refreshed by cron. Intent parsing is invoked adaptively when lexical confidence is low or when the query looks intent-driven. Results are ordered by relevance, with recent records preferred when relevance is comparable.", "operationId": "systemWideSearchGet", "parameters": [ { "in": "query", "name": "query", "required": true, "schema": { "type": "string" }, "description": "Free-text query to search for. Supports natural-language intent fallback and domain synonyms such as `rabat` -> `discount`." }, { "in": "query", "name": "include_types", "required": false, "schema": { "type": "array", "items": { "$ref": "#/components/schemas/SystemSearchEntityType" } }, "style": "form", "explode": false, "description": "Comma-separated list of entity types to include. Defaults to all allowed types." }, { "in": "query", "name": "exclude_types", "required": false, "schema": { "type": "array", "items": { "$ref": "#/components/schemas/SystemSearchEntityType" } }, "style": "form", "explode": false, "description": "Comma-separated list of entity types to exclude." }, { "in": "query", "name": "include_associations", "required": false, "schema": { "type": "boolean", "default": true }, "description": "Include associated objects when matching a primary entity such as a customer." }, { "in": "query", "name": "debug_intent", "required": false, "schema": { "type": "boolean", "default": false }, "description": "Include intent parser diagnostics in `meta.intent_parser`." }, { "in": "query", "name": "limit", "required": false, "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 } }, { "in": "query", "name": "offset", "required": false, "schema": { "type": "integer", "minimum": 0, "default": 0 } } ], "responses": { "200": { "description": "Search results returned successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SystemSearchResponse" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" } } }, "post": { "tags": [ "Search" ], "summary": "System-wide search", "description": "Search across all supported entities using JSON request payload. Customer-related matches include local e-conomic customer index fields. Intent parsing may run adaptively for intent-driven natural-language queries. Results are ordered by relevance, with recent records preferred when relevance is comparable.", "operationId": "systemWideSearchPost", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SystemSearchRequest" } } } }, "responses": { "200": { "description": "Search results returned successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SystemSearchResponse" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" } } } }, "/superuser/search/system/cache": { "delete": { "tags": [ "Search" ], "summary": "Clear system search caches", "description": "Clears both query-result cache and intent-parser cache namespaces for system-wide search.", "operationId": "clearSystemSearchCache", "responses": { "200": { "description": "Cache cleared successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SystemSearchCacheClearResponse" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" } } } }, "/superuser/search/system/cache/rebuild": { "post": { "tags": [ "Search" ], "summary": "Queue system search cache rebuild", "description": "Queues a cache rebuild request and clears active query/intent cache namespaces immediately.", "operationId": "rebuildSystemSearchCache", "requestBody": { "required": false, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SystemSearchCacheRebuildRequest" } } } }, "responses": { "200": { "description": "Cache rebuild queued successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SystemSearchCacheRebuildResponse" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" } } } }, "/economic/config": { "get": { "tags": [ "Config" ], "summary": "Get e-conomic config", "operationId": "getEconomicConfig", "responses": { "200": { "description": "e-conomic configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EconomicConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update e-conomic config", "operationId": "updateEconomicConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "e-conomic configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/reCAPTCHA/config": { "get": { "tags": [ "Config" ], "summary": "Get reCAPTCHA config", "operationId": "getRecaptchaModuleConfig", "responses": { "200": { "description": "reCAPTCHA configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecaptchaConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update reCAPTCHA config", "operationId": "updateRecaptchaConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "reCAPTCHA configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/email/config": { "get": { "tags": [ "Config" ], "summary": "Get email config", "operationId": "getEmailConfig", "responses": { "200": { "description": "Email configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update email config", "operationId": "updateEmailConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Email configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/email/config/test": { "post": { "tags": [ "Config" ], "summary": "Test email config", "operationId": "testEmailConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Email configuration test completed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigTestResponse" } } } } } } }, "/slack/config": { "get": { "tags": [ "Config" ], "summary": "Get Slack config", "operationId": "getSlackConfig", "responses": { "200": { "description": "Slack configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SlackConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update Slack config", "operationId": "updateSlackConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Slack configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/backups/config": { "get": { "tags": [ "Config" ], "summary": "Get backups config", "operationId": "getBackupsConfig", "responses": { "200": { "description": "Backups configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BackupsConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update backups config", "operationId": "updateBackupsConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Backups configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/bird/config": { "get": { "tags": [ "Config" ], "summary": "Get Bird config", "operationId": "getBirdConfig", "responses": { "200": { "description": "Bird configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BirdConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update Bird config", "operationId": "updateBirdConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Bird configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/motorapi/config": { "get": { "tags": [ "Config" ], "summary": "Get MotorAPI config", "operationId": "getMotorApiConfig", "responses": { "200": { "description": "MotorAPI configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MotorApiConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update MotorAPI config", "operationId": "updateMotorApiConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "MotorAPI configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/stripe/config": { "get": { "tags": [ "Config" ], "summary": "Get Stripe config", "operationId": "getStripeConfig", "responses": { "200": { "description": "Stripe configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StripeConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update Stripe config", "operationId": "updateStripeConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Stripe configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/fxratesapi/config": { "get": { "tags": [ "Config" ], "summary": "Get FXRatesAPI config", "operationId": "getFxRatesApiConfig", "responses": { "200": { "description": "FXRatesAPI configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FxRatesApiConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update FXRatesAPI config", "operationId": "updateFxRatesApiConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "FXRatesAPI configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/weatherapi/config": { "get": { "tags": [ "Config" ], "summary": "Get WeatherAPI config", "operationId": "getWeatherApiConfig", "responses": { "200": { "description": "WeatherAPI configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WeatherApiConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update WeatherAPI config", "operationId": "updateWeatherApiConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "WeatherAPI configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/gatewayapi/config": { "get": { "tags": [ "Config" ], "summary": "Get GatewayAPI config", "operationId": "getGatewayApiConfig", "responses": { "200": { "description": "GatewayAPI configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GatewayApiConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update GatewayAPI config", "operationId": "updateGatewayApiConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "GatewayAPI configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/xlvask/config": { "get": { "tags": [ "Config" ], "summary": "Get XLVask config", "operationId": "getXlvaskConfig", "responses": { "200": { "description": "XLVask configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/XlvaskConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update XLVask config", "operationId": "updateXlvaskConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "XLVask configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/entra/config": { "get": { "tags": [ "Config" ], "summary": "Get Entra config", "operationId": "getEntraConfig", "responses": { "200": { "description": "Entra configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EntraConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update Entra config", "operationId": "updateEntraConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Entra configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/limble/config": { "get": { "tags": [ "Config" ], "summary": "Get Limble config", "operationId": "getLimbleConfig", "responses": { "200": { "description": "Limble configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LimbleConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update Limble config", "operationId": "updateLimbleConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Limble configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/ocrspace/config": { "get": { "tags": [ "Config" ], "summary": "Get OcrSpace config", "operationId": "getOcrSpaceConfig", "responses": { "200": { "description": "OcrSpace configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OcrSpaceConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update OcrSpace config", "operationId": "updateOcrSpaceConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "OcrSpace configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/openai/config": { "get": { "tags": [ "Config" ], "summary": "Get OpenAI config", "operationId": "getOpenAiConfig", "responses": { "200": { "description": "OpenAI configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OpenAiConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update OpenAI config", "operationId": "updateOpenAiConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "OpenAI configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/licenseplaterecognizer/config": { "get": { "tags": [ "Config" ], "summary": "Get LicensePlateRecognizer config", "operationId": "getLicensePlateRecognizerConfig", "responses": { "200": { "description": "LicensePlateRecognizer configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LicensePlateRecognizerConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update LicensePlateRecognizer config", "operationId": "updateLicensePlateRecognizerConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "LicensePlateRecognizer configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/virkdata/config": { "get": { "tags": [ "Config" ], "summary": "Get Virkdata config", "operationId": "getVirkdataConfig", "responses": { "200": { "description": "Virkdata configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VirkdataConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update Virkdata config", "operationId": "updateVirkdataConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Virkdata configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/shelly/config": { "get": { "tags": [ "Config" ], "summary": "Get Shelly config", "operationId": "getShellyConfig", "responses": { "200": { "description": "Shelly configuration retrieved successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ShellyConfigListResponse" } } } } } }, "post": { "tags": [ "Config" ], "summary": "Update Shelly config", "operationId": "updateShellyConfig", "requestBody": { "required": false, "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Shelly configuration updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleConfigUpdateResponse" } } } } } } }, "/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/SelfServeConfigListResponse" } } } } } }, "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/ModuleConfigUpdateResponse" } } } } } } }, "/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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } }, "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", "content": { "application/json": { "schema": {} } } }, "403": { "$ref": "#/components/responses/Forbidden" } } } }, "/roles": { "get": { "tags": [ "Roles" ], "summary": "List roles", "operationId": "listRoles", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/modules/washcertificates": { "get": { "tags": [ "Modules" ], "summary": "List wash certificates", "operationId": "listWashCertificates", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/modules/xlvask/services/usage/orders": { "get": { "tags": [ "Modules" ], "summary": "Get XLVask usage orders", "operationId": "getXlvaskUsageOrders", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/modules/xlvask/services/usage/orders/fast-link": { "get": { "tags": [ "Modules" ], "summary": "Get XLVask usage orders fast link", "operationId": "getXlvaskUsageOrdersFastLink", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/superuser/department": { "get": { "tags": [ "Departments" ], "summary": "List departments (superuser)", "operationId": "listSuperuserDepartments", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/departments/daily-reports": { "get": { "tags": [ "Departments" ], "summary": "List daily reports", "operationId": "listDailyReports", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } }, "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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/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", "content": { "application/json": { "schema": {} } } } } } }, "/account/security/passkeys": { "get": { "tags": [ "Security" ], "summary": "List passkeys for the authenticated user", "operationId": "listPasskeys", "responses": { "200": { "description": "A list of passkeys", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Passkey" } } } } }, "400": { "description": "Invalid session or request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } }, "post": { "tags": [ "Security" ], "summary": "Create/add a passkey for the authenticated user", "operationId": "createPasskey", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PasskeyCreateRequest" } } } }, "responses": { "200": { "description": "Passkey created", "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer" } } } } } }, "400": { "description": "Invalid session or request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/account/security/passkeys/{id}": { "patch": { "tags": [ "Security" ], "summary": "Rename a passkey", "operationId": "renamePasskey", "parameters": [ { "in": "path", "name": "id", "required": true, "schema": { "type": "integer" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PasskeyRenameRequest" } } } }, "responses": { "200": { "description": "Passkey renamed", "content": { "application/json": { "schema": {} } } }, "404": { "description": "Not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } }, "delete": { "tags": [ "Security" ], "summary": "Delete a passkey", "operationId": "deletePasskey", "parameters": [ { "in": "path", "name": "id", "required": true, "schema": { "type": "integer" } } ], "responses": { "200": { "description": "Passkey deleted", "content": { "application/json": { "schema": {} } } }, "404": { "description": "Not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } } }, "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" } }, "XCustomerNumber": { "name": "X-Customer-Number", "in": "header", "required": false, "description": "Target customer number for subuser requests. Ignored for classic user sessions.\nRequired on customer-scoped endpoints when authenticated as a subuser unless\nthe target customer can be inferred from context.\n", "schema": { "type": "integer" } } }, "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" } } }, "SystemSearchEntityType": { "type": "string", "enum": [ "objects", "module_config", "orders", "order_items", "customers", "employees", "users", "subusers", "customer_discounts", "customer_fixed_prices", "departments", "permissions", "roles", "invoices", "vehicles", "bookings", "bookings_new", "branding", "categories", "currency_conversion_rates", "customer_codes", "customer_default_department", "customer_notes", "customer_vehicles_addons", "department_categories", "department_daily_reports", "department_gates", "department_goals", "department_lanes", "department_notification_sms", "department_relays", "department_selfserve_condition_rules", "department_selfserve_conditions", "department_selfserve_questions", "department_selfserve_tasks", "department_selfserve_vehicle_conditions", "department_time_bookings_entries", "department_time_bookings_opening_hours", "department_time_bookings_types", "department_variables", "fxratesapi_conversion_rates", "module_action_logs", "motorapi_lookups", "notifications", "order_bookings", "plate_scanners", "plate_scans", "product_options", "products", "stripe_module_customers", "stripe_module_orders", "stripe_payment_intents", "subuser_grants", "xlvask_customers", "xlvask_potential_order_matches", "xlvask_usage_log_wash_items", "xlvask_usage_logs", "xlvask_vehicle_types", "xlvask_vehicles" ] }, "SystemSearchRequest": { "type": "object", "required": [ "query" ], "properties": { "query": { "type": "string", "description": "Free-text query to search for. Customer lookups include local e-conomic index fields and lexical synonym expansion (for example `rabat` -> `discount`)." }, "include_types": { "type": "array", "items": { "$ref": "#/components/schemas/SystemSearchEntityType" }, "description": "Limit search to these entity types." }, "exclude_types": { "type": "array", "items": { "$ref": "#/components/schemas/SystemSearchEntityType" }, "description": "Exclude these entity types from search." }, "include_associations": { "type": "boolean", "default": true, "description": "Include associated records for matched core entities." }, "debug_intent": { "type": "boolean", "default": false, "description": "Include parser diagnostics in `meta.intent_parser`." }, "limit": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 }, "offset": { "type": "integer", "minimum": 0, "default": 0 } } }, "SystemSearchResult": { "type": "object", "properties": { "entity_type": { "$ref": "#/components/schemas/SystemSearchEntityType" }, "entity_id": { "type": "string" }, "title": { "type": "string" }, "description": { "type": "string" }, "customer_number": { "type": "integer", "nullable": true }, "department_id": { "type": "integer", "nullable": true }, "score": { "type": "integer" }, "association_reason": { "type": "string", "nullable": true }, "payload": { "type": "object", "additionalProperties": true } }, "required": [ "entity_type", "entity_id", "title", "score" ] }, "SystemSearchIntentParserMeta": { "type": "object", "properties": { "invoked": { "type": "boolean" }, "source": { "type": "string", "enum": [ "cache", "openai", "none" ] }, "status": { "type": "string" }, "confidence": { "type": "number", "minimum": 0, "maximum": 1 }, "expanded_terms": { "type": "array", "items": { "type": "string" } }, "entity_hints": { "type": "array", "items": { "$ref": "#/components/schemas/SystemSearchEntityType" } }, "fallback_reason": { "type": "string", "nullable": true } }, "required": [ "invoked", "source", "status", "confidence", "expanded_terms", "entity_hints" ] }, "SystemSearchMeta": { "type": "object", "properties": { "query": { "type": "string" }, "limit": { "type": "integer" }, "offset": { "type": "integer" }, "total": { "type": "integer" }, "allowed_types": { "type": "array", "items": { "$ref": "#/components/schemas/SystemSearchEntityType" } }, "cache": { "type": "object", "properties": { "hit": { "type": "boolean" } }, "required": [ "hit" ] }, "intent_parser": { "$ref": "#/components/schemas/SystemSearchIntentParserMeta" } }, "required": [ "query", "limit", "offset", "total", "allowed_types", "cache" ] }, "SystemSearchPayload": { "type": "object", "properties": { "results": { "type": "array", "items": { "$ref": "#/components/schemas/SystemSearchResult" } }, "grouped_results": { "type": "object", "additionalProperties": { "type": "array", "items": { "$ref": "#/components/schemas/SystemSearchResult" } } }, "meta": { "$ref": "#/components/schemas/SystemSearchMeta" } }, "required": [ "results", "grouped_results", "meta" ] }, "SystemSearchResponse": { "type": "object", "properties": { "success": { "type": "boolean" }, "data": { "$ref": "#/components/schemas/SystemSearchPayload" }, "meta": { "type": "object", "additionalProperties": true }, "includes": { "type": "object", "additionalProperties": true } }, "required": [ "success", "data", "meta", "includes" ] }, "SystemSearchCacheClearResponse": { "type": "object", "properties": { "success": { "type": "boolean" }, "data": { "type": "object", "properties": { "message": { "type": "string" }, "query_cache_cleared": { "type": "boolean" }, "intent_cache_cleared": { "type": "boolean" } }, "required": [ "message", "query_cache_cleared", "intent_cache_cleared" ] }, "meta": { "type": "object", "additionalProperties": true }, "includes": { "type": "object", "additionalProperties": true } }, "required": [ "success", "data", "meta", "includes" ] }, "SystemSearchCacheRebuildRequest": { "type": "object", "properties": { "scope": { "type": "string", "enum": [ "all", "types", "dirty" ], "default": "all" }, "types": { "type": "array", "items": { "$ref": "#/components/schemas/SystemSearchEntityType" } } } }, "SystemSearchCacheRebuildResponse": { "type": "object", "properties": { "success": { "type": "boolean" }, "data": { "type": "object", "properties": { "message": { "type": "string" }, "request": { "type": "object", "properties": { "scope": { "type": "string", "enum": [ "all", "types", "dirty" ] }, "types": { "type": "array", "items": { "$ref": "#/components/schemas/SystemSearchEntityType" } }, "requested_at": { "type": "integer" } }, "required": [ "scope", "types", "requested_at" ] }, "query_cache_cleared": { "type": "boolean" }, "intent_cache_cleared": { "type": "boolean" } }, "required": [ "message", "request", "query_cache_cleared", "intent_cache_cleared" ] }, "meta": { "type": "object", "additionalProperties": true }, "includes": { "type": "object", "additionalProperties": true } }, "required": [ "success", "data", "meta", "includes" ] }, "ModuleConfigValue": { "oneOf": [ { "type": "string" }, { "type": "integer" }, { "type": "number" }, { "type": "boolean" }, { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ], "nullable": true }, "ModuleConfigEnvelopeBase": { "type": "object", "properties": { "success": { "type": "boolean" }, "meta": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ] }, "includes": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ] } }, "required": [ "success", "meta", "includes" ] }, "EconomicConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "economic" ] }, "variable": { "type": "string", "enum": [ "adminFeeMonthly", "adminFeeOrder", "feeProductId", "invoiceLayoutNumber", "paymentTermsNumber" ] }, "type": { "type": "string", "enum": [ "string", "int" ] }, "value": { "oneOf": [ { "type": "string" }, { "type": "integer" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "RecaptchaConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "reCAPTCHA" ] }, "variable": { "type": "string", "enum": [ "enabled", "secret_key_v2", "site_key_v2" ] }, "type": { "type": "string", "enum": [ "bool", "string" ] }, "value": { "oneOf": [ { "type": "boolean" }, { "type": "string" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "EmailConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "Email" ] }, "variable": { "type": "string", "enum": [ "enabled", "mailersend_api_key", "mailersend_enabled", "smtp_encryption", "smtp_from", "smtp_from_name", "smtp_host", "smtp_password", "smtp_port", "smtp_reply_to", "smtp_reply_to_name", "smtp_username" ] }, "type": { "type": "string", "enum": [ "bool", "string", "int" ] }, "value": { "oneOf": [ { "type": "boolean" }, { "type": "string" }, { "type": "integer" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "SlackConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "Slack" ] }, "variable": { "type": "string", "enum": [ "customer_registration_webhook_url" ] }, "type": { "type": "string", "enum": [ "string" ] }, "value": { "type": "string", "example": "https://hooks.slack.com/services/..." } }, "required": [ "module", "variable", "type", "value" ] }, "BackupsConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "Backups" ] }, "variable": { "type": "string", "enum": [ "enabled" ] }, "type": { "type": "string", "enum": [ "bool" ] }, "value": { "type": "boolean" } }, "required": [ "module", "variable", "type", "value" ] }, "BirdConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "bird" ] }, "variable": { "type": "string", "enum": [ "api_key", "enabled", "server_url", "workplaceId", "channelId" ] }, "type": { "type": "string", "enum": [ "string", "bool" ] }, "value": { "oneOf": [ { "type": "string" }, { "type": "boolean" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "MotorApiConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "motorapi" ] }, "variable": { "type": "string", "enum": [ "daily_limit", "enabled", "secret_key" ] }, "type": { "type": "string", "enum": [ "int", "bool", "string" ] }, "value": { "oneOf": [ { "type": "integer" }, { "type": "boolean" }, { "type": "string" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "StripeConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "Stripe" ] }, "variable": { "type": "string", "enum": [ "economic_customer_number", "enabled", "publishable_key", "secret_key" ] }, "type": { "type": "string", "enum": [ "int", "bool", "string" ] }, "value": { "oneOf": [ { "type": "integer" }, { "type": "boolean" }, { "type": "string" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "FxRatesApiConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "fxratesapi" ] }, "variable": { "type": "string", "enum": [ "daily_limit", "enabled", "secret_key" ] }, "type": { "type": "string", "enum": [ "int", "bool", "string" ] }, "value": { "oneOf": [ { "type": "integer" }, { "type": "boolean" }, { "type": "string" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "WeatherApiConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "weatherapi" ] }, "variable": { "type": "string", "enum": [ "enabled", "secret_key" ] }, "type": { "type": "string", "enum": [ "bool", "string" ] }, "value": { "oneOf": [ { "type": "boolean" }, { "type": "string" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "GatewayApiConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "GatewayAPI" ] }, "variable": { "type": "string", "enum": [ "api_key", "api_secret", "api_token", "enabled", "sender" ] }, "type": { "type": "string", "enum": [ "string", "bool" ] }, "value": { "oneOf": [ { "type": "string" }, { "type": "boolean" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "XlvaskConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "xlvask" ] }, "variable": { "type": "string", "enum": [ "enabled", "password", "synchronization_enabled", "username" ] }, "type": { "type": "string", "enum": [ "bool", "string" ] }, "value": { "oneOf": [ { "type": "boolean" }, { "type": "string" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "EntraConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "Entra" ] }, "variable": { "type": "string", "enum": [ "enabled", "entra_client_id", "entra_client_secret", "entra_tenant_id" ] }, "type": { "type": "string", "enum": [ "bool", "string" ] }, "value": { "oneOf": [ { "type": "boolean" }, { "type": "string" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "LimbleConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "limble" ] }, "variable": { "type": "string", "enum": [ "client_id", "client_secret", "enabled", "webhooks_enabled" ] }, "type": { "type": "string", "enum": [ "string", "bool" ] }, "value": { "oneOf": [ { "type": "string" }, { "type": "boolean" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "OcrSpaceConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "ocrSpace" ] }, "variable": { "type": "string", "enum": [ "api_key", "enabled" ] }, "type": { "type": "string", "enum": [ "string", "bool" ] }, "value": { "oneOf": [ { "type": "string" }, { "type": "boolean" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "OpenAiConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "openAI" ] }, "variable": { "type": "string", "enum": [ "api_key", "enabled" ] }, "type": { "type": "string", "enum": [ "string", "bool" ] }, "value": { "oneOf": [ { "type": "string" }, { "type": "boolean" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "LicensePlateRecognizerConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "licenseplaterecognizer" ] }, "variable": { "type": "string", "enum": [ "api_key", "enabled" ] }, "type": { "type": "string", "enum": [ "string", "bool" ] }, "value": { "oneOf": [ { "type": "string" }, { "type": "boolean" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "VirkdataConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "virkdata" ] }, "variable": { "type": "string", "enum": [ "enabled", "monthly_limit", "secret_key" ] }, "type": { "type": "string", "enum": [ "bool", "int", "string" ] }, "value": { "oneOf": [ { "type": "boolean" }, { "type": "integer" }, { "type": "string" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "ShellyConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "shelly" ] }, "variable": { "type": "string", "enum": [ "enabled", "secret_key", "server_url" ] }, "type": { "type": "string", "enum": [ "bool", "string" ] }, "value": { "oneOf": [ { "type": "boolean" }, { "type": "string" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "SelfServeConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "enum": [ "selfserve" ] }, "variable": { "type": "string", "enum": [ "enabled", "minute_product" ] }, "type": { "type": "string", "enum": [ "bool", "int" ] }, "value": { "oneOf": [ { "type": "boolean" }, { "type": "integer" } ] } }, "required": [ "module", "variable", "type", "value" ] }, "EconomicConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/EconomicConfigEntry" } } }, "required": [ "data" ] } ] }, "RecaptchaConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/RecaptchaConfigEntry" } } }, "required": [ "data" ] } ] }, "EmailConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/EmailConfigEntry" } } }, "required": [ "data" ] } ] }, "SlackConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/SlackConfigEntry" } } }, "required": [ "data" ] } ] }, "BackupsConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/BackupsConfigEntry" } } }, "required": [ "data" ] } ] }, "BirdConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/BirdConfigEntry" } } }, "required": [ "data" ] } ] }, "MotorApiConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/MotorApiConfigEntry" } } }, "required": [ "data" ] } ] }, "StripeConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/StripeConfigEntry" } } }, "required": [ "data" ] } ] }, "FxRatesApiConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/FxRatesApiConfigEntry" } } }, "required": [ "data" ] } ] }, "WeatherApiConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/WeatherApiConfigEntry" } } }, "required": [ "data" ] } ] }, "GatewayApiConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/GatewayApiConfigEntry" } } }, "required": [ "data" ] } ] }, "XlvaskConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/XlvaskConfigEntry" } } }, "required": [ "data" ] } ] }, "EntraConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/EntraConfigEntry" } } }, "required": [ "data" ] } ] }, "LimbleConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/LimbleConfigEntry" } } }, "required": [ "data" ] } ] }, "OcrSpaceConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/OcrSpaceConfigEntry" } } }, "required": [ "data" ] } ] }, "OpenAiConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/OpenAiConfigEntry" } } }, "required": [ "data" ] } ] }, "LicensePlateRecognizerConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/LicensePlateRecognizerConfigEntry" } } }, "required": [ "data" ] } ] }, "VirkdataConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/VirkdataConfigEntry" } } }, "required": [ "data" ] } ] }, "ShellyConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/ShellyConfigEntry" } } }, "required": [ "data" ] } ] }, "SelfServeConfigListResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/SelfServeConfigEntry" } } }, "required": [ "data" ] } ] }, "DepartmentWeatherStatus": { "type": "string", "enum": [ "unknown", "healthy", "degraded", "unhealthy" ] }, "DepartmentWeatherCondition": { "type": "string", "enum": [ "clear", "mostly_clear", "partly_cloudy", "mostly_cloudy", "overcast", "rain", "showers", "thunderstorm", "snow", "fog" ] }, "DepartmentWeatherTimelineEntry": { "type": "object", "properties": { "time": { "type": "string", "example": "01:00" }, "weather": { "$ref": "#/components/schemas/DepartmentWeatherCondition" }, "washes": { "type": "integer", "minimum": 0, "example": 0 }, "hours": { "type": "integer", "minimum": 0, "example": 10 }, "status": { "$ref": "#/components/schemas/DepartmentWeatherStatus" } }, "required": [ "time", "weather", "washes", "hours", "status" ] }, "WeatherApiObjectResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "object", "additionalProperties": true } }, "required": [ "data" ] } ] }, "DepartmentWeatherTimelineResponse": { "allOf": [ { "$ref": "#/components/schemas/ModuleConfigEnvelopeBase" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/DepartmentWeatherTimelineEntry" } } }, "required": [ "data" ] } ] }, "ModuleConfigEntry": { "type": "object", "properties": { "module": { "type": "string", "description": "Module name" }, "variable": { "type": "string", "description": "Configuration variable key" }, "type": { "type": "string", "description": "Stored value type in module_config" }, "value": { "$ref": "#/components/schemas/ModuleConfigValue" } }, "required": [ "module", "variable", "type", "value" ] }, "ModuleConfigListResponse": { "type": "object", "properties": { "success": { "type": "boolean" }, "data": { "type": "array", "items": { "$ref": "#/components/schemas/ModuleConfigEntry" } }, "meta": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ] }, "includes": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ] } }, "required": [ "success", "data", "meta", "includes" ] }, "ModuleConfigUpdateResponse": { "type": "object", "properties": { "success": { "type": "boolean" }, "data": { "type": "boolean" }, "meta": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ] }, "includes": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ] } }, "required": [ "success", "data", "meta", "includes" ] }, "ModuleConfigTestResponse": { "type": "object", "properties": { "success": { "type": "boolean" }, "data": { "type": "string" }, "meta": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ] }, "includes": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ] } }, "required": [ "success", "data", "meta", "includes" ] }, "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" } } }, "SubuserGrant": { "type": "object", "properties": { "id": { "type": "integer" }, "billing_customer_number": { "type": "integer", "description": "e-conomic customer number" }, "subuser": { "type": "integer", "description": "Subuser ID" }, "enabled": { "type": "boolean" }, "note": { "type": "string", "nullable": true }, "permissions": { "type": "array", "description": "List of permission node keys", "items": { "type": "string", "example": "BOOKINGS_LIST" } }, "created_at": { "type": "string", "format": "date-time", "nullable": true }, "updated_at": { "type": "string", "format": "date-time", "nullable": true }, "deleted_at": { "type": "string", "format": "date-time", "nullable": true } } }, "SubuserGrantCreateRequest": { "type": "object", "required": [ "customer_number", "subuser_id" ], "properties": { "customer_number": { "type": "integer", "description": "e-conomic customer number" }, "subuser_id": { "type": "integer", "description": "Subuser ID to grant permissions for" }, "enabled": { "type": "boolean", "default": true }, "note": { "type": "string", "nullable": true, "maxLength": 65535 }, "permissions": { "type": "array", "description": "Optional list of permission keys; defaults will be applied if omitted", "items": { "type": "string" } } } }, "SubuserGrantUpdateRequest": { "type": "object", "properties": { "enabled": { "type": "boolean" }, "note": { "type": "string", "nullable": true, "maxLength": 65535 }, "permissions": { "type": "array", "items": { "type": "string" } } } }, "SubuserGrantSummary": { "type": "object", "description": "Summary of a subuser grant grouped by billing customer number", "properties": { "billing_customer_number": { "type": "integer", "description": "e-conomic customer number this grant applies to" }, "permissions": { "type": "array", "description": "List of permission node keys enabled for this customer", "items": { "type": "string" } } } }, "SubuserSelf": { "type": "object", "description": "Authenticated subuser profile with enabled grants", "properties": { "id": { "type": "integer" }, "username": { "type": "string" }, "name": { "type": "string", "nullable": true }, "email": { "type": "string", "format": "email", "nullable": true }, "phone_country_code": { "type": "integer", "nullable": true }, "phone": { "type": "integer", "nullable": true }, "grants": { "type": "array", "description": "Enabled, non-deleted grants for the subuser grouped by billing customer number", "items": { "$ref": "#/components/schemas/SubuserGrantSummary" } }, "created_at": { "type": "string", "format": "date-time", "nullable": true }, "updated_at": { "type": "string", "format": "date-time", "nullable": true }, "suspended_at": { "type": "string", "format": "date-time", "nullable": true }, "two_factor_enabled": { "type": "boolean", "description": "Indicates if 2FA is enabled for this account" } } }, "PermissionNode": { "type": "object", "properties": { "key": { "type": "string", "description": "Permission node key" }, "name": { "type": "string" }, "description": { "type": "string" }, "type": { "type": "string", "description": "Permission type (e.g., TOGGLE)" }, "default": { "type": "boolean" } } }, "PermissionNodeGroup": { "type": "object", "properties": { "group": { "type": "string" }, "description": { "type": "string" }, "nodes": { "type": "array", "items": { "$ref": "#/components/schemas/PermissionNode" } } } }, "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" } } }, "CollectedInvoiceEconomicCompareResponse": { "type": "object", "description": "Result of comparing a collected invoice with its E-conomic counterpart", "properties": { "collected_invoice_id": { "type": "integer", "description": "The internal collected invoice ID", "example": 123 }, "draft_id": { "type": "integer", "nullable": true, "description": "E-conomic draft invoice ID, if present", "example": 456 }, "booked_id": { "type": "integer", "nullable": true, "description": "E-conomic booked invoice ID, if present", "example": 28368 }, "warnings": { "type": "array", "description": "List of warnings detected during comparison", "items": { "type": "string" }, "example": [ "Total amount mismatch for draft invoice ID 456: E-Conomic total is 867.5, internal total is 694" ] }, "draft_total": { "type": "number", "format": "float", "nullable": true, "description": "Total amount from the E-conomic draft (gross)", "example": 867.5 }, "booked_total": { "type": "number", "format": "float", "nullable": true, "description": "Total amount from the E-conomic booked invoice (gross minus VAT if applicable)", "example": 694 }, "difference": { "type": "number", "format": "float", "nullable": true, "description": "Selected e-conomic total (draft when available, otherwise booked) minus internal_total", "example": 0 }, "internal_total": { "type": "number", "format": "float", "description": "Internal total amount for the collected invoice", "example": 694 } }, "required": [ "collected_invoice_id", "internal_total" ] }, "CollectedInvoiceEconomicV2DetailsResponse": { "type": "object", "properties": { "collected_invoice_id": { "type": "integer" }, "external_id": { "type": "string" }, "order_ids": { "type": "array", "items": { "type": "integer" } }, "economic": { "type": "object", "properties": { "draft_id": { "type": "integer", "nullable": true }, "booked_id": { "type": "integer", "nullable": true } } }, "customer": { "$ref": "#/components/schemas/CollectedInvoiceEconomicV2CustomerSummary" }, "internal": { "type": "object", "required": [ "normalized" ], "properties": { "normalized": { "$ref": "#/components/schemas/EconomicV2NormalizedInvoice" } } }, "draft": { "type": "object", "properties": { "exists": { "type": "boolean" }, "raw": { "type": "object", "nullable": true, "additionalProperties": true }, "normalized": { "allOf": [ { "$ref": "#/components/schemas/EconomicV2NormalizedInvoice" } ], "nullable": true } } }, "booked": { "type": "object", "properties": { "exists": { "type": "boolean" }, "raw": { "type": "object", "nullable": true, "additionalProperties": true }, "normalized": { "allOf": [ { "$ref": "#/components/schemas/EconomicV2NormalizedInvoice" } ], "nullable": true } } }, "warnings": { "type": "array", "items": { "type": "string" } } }, "required": [ "collected_invoice_id", "order_ids", "economic", "customer", "internal", "draft", "booked", "warnings" ] }, "CollectedInvoiceEconomicV2CustomerSummary": { "type": "object", "properties": { "internal_customer_number": { "type": "integer", "nullable": true }, "draft_customer_number": { "type": "integer", "nullable": true }, "booked_customer_number": { "type": "integer", "nullable": true }, "exists": { "type": "boolean" }, "name": { "type": "string", "nullable": true }, "barred": { "type": "boolean", "nullable": true } }, "required": [ "exists" ] }, "CollectedInvoiceEconomicV2CompareResponse": { "type": "object", "properties": { "collected_invoice_id": { "type": "integer" }, "details": { "$ref": "#/components/schemas/CollectedInvoiceEconomicV2DetailsResponse" }, "comparison": { "$ref": "#/components/schemas/EconomicV2Comparison" }, "warnings": { "type": "array", "items": { "type": "string" } } }, "required": [ "collected_invoice_id", "details", "comparison", "warnings" ] }, "CollectedInvoiceEconomicV2CompareBulkResponse": { "type": "object", "properties": { "requested": { "type": "integer" }, "compared": { "type": "integer" }, "failed": { "type": "integer" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/CollectedInvoiceEconomicV2CompareResponse" } }, "errors": { "type": "array", "items": { "type": "object", "properties": { "collected_invoice_id": { "type": "integer" }, "error": { "type": "string" } } } } }, "required": [ "requested", "compared", "failed", "results", "errors" ] }, "CollectedInvoiceEconomicV2RevenueStatisticsResponse": { "type": "object", "properties": { "filters": { "type": "object", "properties": { "dateFrom": { "type": "string", "format": "date" }, "dateTo": { "type": "string", "format": "date" }, "customer_numbers": { "type": "array", "items": { "type": "integer" } }, "department_numbers": { "type": "array", "items": { "type": "integer" } }, "currency": { "type": "string", "nullable": true }, "barred": { "type": "string", "enum": [ "all", "barred", "active" ] }, "max_pages": { "type": "integer" } } }, "summary": { "$ref": "#/components/schemas/EconomicV2RevenueSummary" }, "customers": { "type": "array", "items": { "$ref": "#/components/schemas/EconomicV2RevenueCustomerStat" } }, "departments": { "type": "array", "items": { "$ref": "#/components/schemas/EconomicV2RevenueDepartmentStat" } }, "currencies": { "type": "array", "items": { "$ref": "#/components/schemas/EconomicV2RevenueCurrencyStat" } }, "warnings": { "type": "array", "items": { "type": "string" } } }, "required": [ "filters", "summary", "customers", "departments", "currencies", "warnings" ] }, "EconomicV2RevenueSummary": { "type": "object", "properties": { "invoice_count": { "type": "integer" }, "line_count": { "type": "integer" }, "unique_customers": { "type": "integer" }, "net_amount": { "type": "number" }, "vat_amount": { "type": "number" }, "gross_amount": { "type": "number" }, "average_invoice_net_amount": { "type": "number" } }, "required": [ "invoice_count", "line_count", "unique_customers", "net_amount", "vat_amount", "gross_amount", "average_invoice_net_amount" ] }, "EconomicV2RevenueCustomerStat": { "type": "object", "properties": { "customer_number": { "type": "integer" }, "customer_name": { "type": "string", "nullable": true }, "barred": { "type": "boolean", "nullable": true }, "invoice_count": { "type": "integer" }, "net_amount": { "type": "number" }, "vat_amount": { "type": "number" }, "gross_amount": { "type": "number" } }, "required": [ "customer_number", "invoice_count", "net_amount", "vat_amount", "gross_amount" ] }, "EconomicV2RevenueDepartmentStat": { "type": "object", "properties": { "department_key": { "type": "string" }, "department_number": { "type": "integer", "nullable": true }, "invoice_count": { "type": "integer" }, "line_count": { "type": "integer" }, "net_amount": { "type": "number" }, "vat_amount": { "type": "number" }, "gross_amount": { "type": "number" } }, "required": [ "department_key", "invoice_count", "line_count", "net_amount", "vat_amount", "gross_amount" ] }, "EconomicV2RevenueCurrencyStat": { "type": "object", "properties": { "currency": { "type": "string" }, "invoice_count": { "type": "integer" }, "net_amount": { "type": "number" }, "vat_amount": { "type": "number" }, "gross_amount": { "type": "number" } }, "required": [ "currency", "invoice_count", "net_amount", "vat_amount", "gross_amount" ] }, "EconomicV2NormalizedInvoice": { "type": "object", "properties": { "source": { "type": "string", "enum": [ "internal", "draft", "booked" ] }, "totals": { "type": "object", "properties": { "net_total": { "type": "number" }, "line_net_total": { "type": "number" }, "line_count": { "type": "integer" }, "billable_line_count": { "type": "integer" }, "difference_from_line_sum": { "type": "number", "nullable": true } } }, "departments": { "$ref": "#/components/schemas/EconomicV2DepartmentDistribution" }, "lines": { "type": "array", "items": { "$ref": "#/components/schemas/EconomicV2NormalizedLineItem" } }, "warnings": { "type": "array", "items": { "type": "string" } } }, "required": [ "source", "totals", "departments", "lines", "warnings" ] }, "EconomicV2NormalizedLineItem": { "type": "object", "properties": { "index": { "type": "integer" }, "source": { "type": "string" }, "source_order_id": { "type": "integer", "nullable": true }, "source_line_id": { "type": "integer", "nullable": true }, "line_type": { "type": "string", "enum": [ "product", "discount", "text" ] }, "billable": { "type": "boolean" }, "product_number": { "type": "string", "nullable": true }, "product_id": { "type": "integer", "nullable": true }, "description": { "type": "string" }, "reference": { "type": "string" }, "quantity": { "type": "number" }, "unit_net_price": { "type": "number" }, "line_net_amount": { "type": "number" }, "department_distribution": { "$ref": "#/components/schemas/EconomicV2DepartmentDistribution" }, "match_key": { "type": "string" } }, "required": [ "source", "line_type", "billable", "description", "reference", "quantity", "unit_net_price", "line_net_amount", "department_distribution", "match_key" ] }, "EconomicV2DepartmentDistribution": { "type": "object", "additionalProperties": { "type": "number" }, "example": { "75": 100 } }, "EconomicV2Comparison": { "type": "object", "properties": { "totals": { "type": "object", "properties": { "internal_net_total": { "type": "number" } } }, "targets": { "type": "object", "properties": { "draft": { "$ref": "#/components/schemas/EconomicV2TargetComparison" }, "booked": { "$ref": "#/components/schemas/EconomicV2TargetComparison" } } }, "warnings": { "type": "array", "items": { "type": "string" } } }, "required": [ "totals", "targets", "warnings" ] }, "EconomicV2TargetComparison": { "type": "object", "properties": { "target": { "type": "string", "enum": [ "draft", "booked" ] }, "status": { "type": "string", "enum": [ "exact_match", "partial_mismatch", "total_mismatch", "missing_target" ] }, "overall_match": { "type": "boolean" }, "totals": { "$ref": "#/components/schemas/EconomicV2TotalsComparison" }, "lines": { "type": "object", "properties": { "summary": { "type": "object", "properties": { "internal_billable_count": { "type": "integer" }, "target_billable_count": { "type": "integer" }, "mismatch_count": { "type": "integer" } } }, "diff": { "type": "array", "items": { "$ref": "#/components/schemas/EconomicV2LineDiffEntry" } } } }, "departments": { "type": "object", "properties": { "matches": { "type": "boolean" }, "diff": { "type": "array", "items": { "$ref": "#/components/schemas/EconomicV2DepartmentDiffEntry" } } } }, "mismatch_reasons": { "type": "array", "items": { "type": "string" } }, "warnings": { "type": "array", "items": { "type": "string" } } }, "required": [ "target", "status", "overall_match", "totals", "lines", "departments", "mismatch_reasons", "warnings" ] }, "EconomicV2TotalsComparison": { "type": "object", "properties": { "internal_net_total": { "type": "number", "nullable": true }, "target_net_total": { "type": "number", "nullable": true }, "difference": { "type": "number", "nullable": true }, "abs_difference": { "type": "number", "nullable": true }, "matches": { "type": "boolean" } }, "required": [ "matches" ] }, "EconomicV2LineDiffEntry": { "type": "object", "properties": { "match_key": { "type": "string" }, "reasons": { "type": "array", "items": { "type": "string" } }, "internal_line": { "allOf": [ { "$ref": "#/components/schemas/EconomicV2NormalizedLineItem" } ], "nullable": true }, "target_line": { "allOf": [ { "$ref": "#/components/schemas/EconomicV2NormalizedLineItem" } ], "nullable": true } }, "required": [ "match_key", "reasons" ] }, "EconomicV2DepartmentDiffEntry": { "type": "object", "properties": { "department_key": { "type": "string" }, "internal_amount": { "type": "number" }, "target_amount": { "type": "number" }, "difference": { "type": "number" }, "matches": { "type": "boolean" } }, "required": [ "department_key", "internal_amount", "target_amount", "difference", "matches" ] }, "InvoicingDistributionV2Transaction": { "type": "object", "properties": { "id": { "type": "integer" }, "date": { "type": "string", "format": "date-time" }, "amount": { "type": "number" }, "booked": { "type": "boolean" }, "department_id": { "type": "integer" }, "excluded": { "type": "boolean" } }, "required": [ "id", "date", "amount", "booked", "department_id", "excluded" ] }, "InvoicingDistributionV2Customer": { "type": "object", "properties": { "id": { "type": "integer", "nullable": true }, "customer_number": { "type": "integer" }, "customer_name": { "type": "string" }, "transactions": { "type": "array", "items": { "$ref": "#/components/schemas/InvoicingDistributionV2Transaction" } }, "requires_action": { "type": "boolean" }, "meta": { "type": "object", "additionalProperties": true } }, "required": [ "customer_number", "customer_name", "transactions", "requires_action", "meta" ] }, "InvoicingDistributionV2CategoryResponse": { "type": "object", "properties": { "customers": { "type": "array", "items": { "$ref": "#/components/schemas/InvoicingDistributionV2Customer" } }, "collective_results": { "type": "object", "additionalProperties": true }, "warnings": { "type": "array", "items": { "type": "string" } } }, "required": [ "customers", "collective_results", "warnings" ] }, "InvoicingDistributionV2FixedPricingResponse": { "$ref": "#/components/schemas/InvoicingDistributionV2CategoryResponse" }, "InvoicingDistributionV2WashSubscriptionsResponse": { "$ref": "#/components/schemas/InvoicingDistributionV2CategoryResponse" }, "InvoicingDistributionV2CustomerPricesResponse": { "$ref": "#/components/schemas/InvoicingDistributionV2CategoryResponse" }, "InvoicingDistributionV2BookedDepartment75Group": { "type": "object", "properties": { "month": { "type": "string", "example": "2026-01" }, "source_category": { "type": "string", "enum": [ "fixed_pricing", "wash_subscriptions", "unclassified" ] }, "invoice_ids": { "type": "array", "items": { "type": "integer" } }, "booked_net_amount": { "type": "number" }, "department_distribution": { "$ref": "#/components/schemas/EconomicV2DepartmentDistribution" }, "undistributed_net_amount": { "type": "number" } }, "required": [ "month", "source_category", "invoice_ids", "booked_net_amount", "department_distribution", "undistributed_net_amount" ] }, "InvoicingDistributionV2BookedDepartment75Meta": { "type": "object", "properties": { "booked_net_amount": { "type": "number" }, "distributed_net_amount": { "type": "number" }, "undistributed_net_amount": { "type": "number" }, "department_distribution": { "$ref": "#/components/schemas/EconomicV2DepartmentDistribution" }, "booked_groups": { "type": "array", "items": { "$ref": "#/components/schemas/InvoicingDistributionV2BookedDepartment75Group" } } }, "required": [ "booked_net_amount", "distributed_net_amount", "undistributed_net_amount", "department_distribution", "booked_groups" ] }, "InvoicingDistributionV2BookedDepartment75Customer": { "allOf": [ { "$ref": "#/components/schemas/InvoicingDistributionV2Customer" }, { "type": "object", "properties": { "meta": { "type": "object", "properties": { "booked_department_75": { "$ref": "#/components/schemas/InvoicingDistributionV2BookedDepartment75Meta" } }, "required": [ "booked_department_75" ] } } } ] }, "InvoicingDistributionV2BookedDepartment75CollectiveResults": { "type": "object", "properties": { "booked_net_amount": { "type": "number" }, "distributed_net_amount": { "type": "number" }, "undistributed_net_amount": { "type": "number" }, "department_distribution": { "$ref": "#/components/schemas/EconomicV2DepartmentDistribution" }, "department_distribution_parsed": { "type": "object", "additionalProperties": { "type": "number" } } }, "required": [ "booked_net_amount", "distributed_net_amount", "undistributed_net_amount", "department_distribution", "department_distribution_parsed" ] }, "InvoicingDistributionV2BookedDepartment75Response": { "type": "object", "properties": { "customers": { "type": "array", "items": { "$ref": "#/components/schemas/InvoicingDistributionV2BookedDepartment75Customer" } }, "collective_results": { "$ref": "#/components/schemas/InvoicingDistributionV2BookedDepartment75CollectiveResults" }, "warnings": { "type": "array", "items": { "type": "string" } } }, "required": [ "customers", "collective_results", "warnings" ] }, "InvoicingDistributionV2AllResponse": { "type": "object", "properties": { "fixed_pricing": { "$ref": "#/components/schemas/InvoicingDistributionV2FixedPricingResponse" }, "wash_subscriptions": { "$ref": "#/components/schemas/InvoicingDistributionV2WashSubscriptionsResponse" }, "customer_prices": { "$ref": "#/components/schemas/InvoicingDistributionV2CustomerPricesResponse" }, "booked_department_75": { "$ref": "#/components/schemas/InvoicingDistributionV2BookedDepartment75Response" } }, "required": [ "fixed_pricing", "wash_subscriptions", "customer_prices", "booked_department_75" ] }, "PricingHistoryVersionEntry": { "type": "object", "properties": { "id": { "type": "integer" }, "type": { "type": "string", "enum": [ "fixed_pricing", "vehicle_subscription", "discount_override" ] }, "customer_number": { "type": "integer" }, "effective_from": { "type": "string", "format": "date-time" }, "effective_to": { "type": "string", "format": "date-time", "nullable": true }, "source": { "type": "string" }, "confidence": { "type": "number", "minimum": 0, "maximum": 1 }, "inferred": { "type": "boolean" }, "metadata_json": { "oneOf": [ { "type": "string" }, { "type": "object", "additionalProperties": true }, { "type": "array", "items": {} } ], "nullable": true } }, "required": [ "id", "type", "customer_number", "effective_from", "source", "confidence", "inferred" ] }, "CustomerPricingHistoryResponse": { "type": "object", "properties": { "customer_number": { "type": "integer" }, "fixed_pricing": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "vehicle_subscriptions": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "discount_overrides": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "timeline": { "type": "array", "items": { "$ref": "#/components/schemas/PricingHistoryVersionEntry" } } }, "required": [ "customer_number", "fixed_pricing", "vehicle_subscriptions", "discount_overrides", "timeline" ] }, "InvoicingWashSubscriptionsDistributionResponse": { "type": "object", "properties": { "success": { "type": "boolean", "example": true }, "data": { "type": "array", "items": { "$ref": "#/components/schemas/InvoicingWashSubscriptionsDistributionCustomer" } }, "meta": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionMeta" }, "includes": { "type": "object", "additionalProperties": true } }, "required": [ "success", "data", "meta", "includes" ] }, "InvoicingWashSubscriptionsDistributionCustomer": { "type": "object", "properties": { "id": { "type": "integer", "nullable": true }, "customer_number": { "type": "integer" }, "customer_name": { "type": "string" }, "transactions": { "type": "array", "items": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionTransaction" } }, "requires_action": { "type": "boolean" }, "meta": { "type": "object", "properties": { "subscription": { "type": "object", "additionalProperties": true } }, "required": [ "subscription" ] } }, "required": [ "customer_number", "customer_name", "transactions", "requires_action", "meta" ] }, "InvoicingFixedPricingDistributionResponse": { "type": "object", "properties": { "success": { "type": "boolean", "example": true }, "data": { "type": "array", "items": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionCustomer" } }, "meta": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionMeta" }, "includes": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionIncludes" } }, "required": [ "success", "data", "meta", "includes" ] }, "InvoicingFixedPricingDistributionCustomer": { "type": "object", "properties": { "id": { "type": "integer" }, "customer_number": { "type": "integer" }, "customer_name": { "type": "string" }, "transactions": { "type": "array", "items": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionTransaction" } }, "requires_action": { "type": "boolean" }, "meta": { "type": "object", "properties": { "fixed_pricing": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionFixedPricing" } }, "required": [ "fixed_pricing" ] } }, "required": [ "id", "customer_number", "customer_name", "transactions", "requires_action", "meta" ] }, "InvoicingFixedPricingDistributionTransaction": { "type": "object", "properties": { "id": { "type": "integer" }, "date": { "type": "string", "description": "Datetime in `YYYY-MM-DD HH:mm:ss` format.", "example": "2026-02-02 10:43:41" }, "amount": { "type": "number" }, "booked": { "type": "boolean" }, "excluded": { "type": "boolean" } }, "required": [ "id", "date", "amount", "booked", "excluded" ] }, "InvoicingFixedPricingDistributionFixedPricing": { "type": "object", "properties": { "customer_number": { "type": "integer" }, "price": { "type": "number" }, "description": { "type": "string" }, "original_price": { "type": "number" }, "department_totals": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionNumberMapOrEmptyArray" }, "department_totals_relative": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionNumberMapOrEmptyArray" } }, "required": [ "customer_number", "price", "description", "original_price", "department_totals", "department_totals_relative" ] }, "InvoicingFixedPricingDistributionMeta": { "type": "object", "properties": { "date_from": { "type": "string", "description": "Datetime in `YYYY-MM-DD HH:mm:ss` format.", "example": "2026-02-01 00:00:00" }, "date_to": { "type": "string", "description": "Datetime in `YYYY-MM-DD HH:mm:ss` format.", "example": "2026-02-28 23:59:59" } }, "required": [ "date_from", "date_to" ] }, "InvoicingFixedPricingDistributionIncludes": { "type": "object", "properties": { "debug_invoicing_period_customers_with_orders_in_date_range": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionExecutionTime" }, "debug_invoicing_period_process_customer_numbers": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionExecutionTime" }, "debug_invoicing_period_get_transactions_for_customers_in_date_range": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionExecutionTime" }, "debug_invoicing_period_calculate_transaction_totals": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionExecutionTime" }, "debug_invoicing_period_construct_customer_objects": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionExecutionTime" }, "collective_fixed_pricing_results": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionCollectiveResults" } }, "additionalProperties": true }, "InvoicingFixedPricingDistributionExecutionTime": { "type": "object", "properties": { "execution_time": { "type": "number" } }, "required": [ "execution_time" ] }, "InvoicingFixedPricingDistributionCollectiveResults": { "type": "object", "properties": { "total_fixed_price": { "type": "number" }, "total_original_price": { "type": "number" }, "total_department_totals": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionNumberMapOrEmptyArray" }, "total_department_totals_relative": { "$ref": "#/components/schemas/InvoicingFixedPricingDistributionNumberMapOrEmptyArray" }, "total_department_totals_parsed": { "type": "object", "additionalProperties": { "type": "number" } }, "total_department_totals_relative_parsed": { "type": "object", "additionalProperties": { "type": "number" } } }, "required": [ "total_fixed_price", "total_original_price", "total_department_totals", "total_department_totals_relative", "total_department_totals_parsed", "total_department_totals_relative_parsed" ] }, "InvoicingFixedPricingDistributionNumberMapOrEmptyArray": { "oneOf": [ { "type": "object", "additionalProperties": { "type": "number" } }, { "type": "array", "maxItems": 0 } ] }, "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" } } }, "SelfserveLaneService": { "type": "string", "description": "Allowed self-serve lane service name", "enum": [ "MACHINE" ] }, "SelfserveMachineType": { "type": "object", "properties": { "id": { "type": "integer" }, "name": { "type": "string" }, "description": { "type": "string", "nullable": true }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time", "nullable": true } } }, "SelfserveVisibleQuestion": { "type": "object", "properties": { "id": { "type": "integer" }, "question": { "type": "string" }, "description": { "type": "string" }, "condition_id": { "type": "integer", "nullable": true }, "order_priority": { "type": "integer" }, "answer": { "type": "boolean", "nullable": true } } }, "SelfserveTaskDecision": { "type": "object", "properties": { "id": { "type": "integer" }, "task": { "type": "string" }, "description": { "type": "string" }, "condition_id": { "type": "integer", "nullable": true }, "order_priority": { "type": "integer" }, "services": { "type": "array", "items": { "$ref": "#/components/schemas/SelfserveLaneService" } }, "buttons": { "type": "array", "items": { "type": "integer" } } } }, "SelfserveWashSession": { "type": "object", "properties": { "id": { "type": "integer" }, "lane_id": { "type": "integer" }, "department_id": { "type": "integer" }, "machine_type_id": { "type": "integer", "nullable": true }, "customer_number": { "type": "integer", "nullable": true }, "vehicle_id": { "type": "integer", "nullable": true }, "vehicle_type_id": { "type": "integer", "nullable": true }, "reg": { "type": "string" }, "status": { "type": "string" }, "allowed": { "type": "boolean" }, "machine_relay_enabled": { "type": "boolean" }, "machine_relay_enabled_at": { "type": "string", "format": "date-time", "nullable": true }, "machine_start_triggered": { "type": "boolean" }, "machine_start_triggered_at": { "type": "string", "format": "date-time", "nullable": true }, "order_id": { "type": "integer", "nullable": true }, "completed_at": { "type": "string", "format": "date-time", "nullable": true }, "metadata": { "type": "object", "additionalProperties": true }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time", "nullable": true } } }, "SelfserveWashQuestionAnswer": { "type": "object", "properties": { "question_id": { "type": "integer" }, "question": { "type": "string" }, "answer": { "type": "boolean" }, "answered_at": { "type": "string", "format": "date-time" } } }, "SelfserveWashTaskSnapshot": { "type": "object", "properties": { "task_id": { "type": "integer", "nullable": true }, "task": { "type": "string" }, "description": { "type": "string", "nullable": true }, "services": { "type": "array", "items": { "$ref": "#/components/schemas/SelfserveLaneService" } }, "buttons": { "type": "array", "items": { "type": "integer" } } } }, "SelfserveWashEvent": { "type": "object", "properties": { "id": { "type": "integer" }, "type": { "type": "string" }, "payload": { "type": "object", "additionalProperties": true, "nullable": true }, "created_at": { "type": "string", "format": "date-time" } } }, "SelfserveVehicleAllowedResponse": { "type": "object", "properties": { "lane": { "$ref": "#/components/schemas/DepartmentLane" }, "machine_type": { "allOf": [ { "$ref": "#/components/schemas/SelfserveMachineType" } ], "nullable": true }, "vehicle": { "type": "object", "additionalProperties": true, "nullable": true }, "reg": { "type": "string" }, "customer_number": { "type": "integer", "nullable": true }, "questions": { "type": "array", "items": { "$ref": "#/components/schemas/SelfserveVisibleQuestion" } }, "tasks": { "type": "array", "items": { "$ref": "#/components/schemas/SelfserveTaskDecision" } }, "allowed_services": { "type": "array", "items": { "$ref": "#/components/schemas/SelfserveLaneService" } }, "machine_available": { "type": "boolean" }, "all_visible_questions_answered": { "type": "boolean" }, "allowed": { "type": "boolean" }, "session": { "allOf": [ { "$ref": "#/components/schemas/SelfserveWashSession" } ], "nullable": true } } }, "SelfserveWashSummary": { "type": "object", "properties": { "session": { "$ref": "#/components/schemas/SelfserveWashSession" }, "lane": { "allOf": [ { "$ref": "#/components/schemas/DepartmentLane" } ], "nullable": true }, "machine_type": { "allOf": [ { "$ref": "#/components/schemas/SelfserveMachineType" } ], "nullable": true }, "questions": { "type": "array", "items": { "$ref": "#/components/schemas/SelfserveWashQuestionAnswer" } }, "tasks": { "type": "array", "items": { "$ref": "#/components/schemas/SelfserveWashTaskSnapshot" } }, "events": { "type": "array", "items": { "$ref": "#/components/schemas/SelfserveWashEvent" } } } }, "DepartmentSelfserveVehicleConditionMutationResponse": { "type": "object", "properties": { "condition": { "$ref": "#/components/schemas/DepartmentSelfserveVehicleCondition" }, "selfserve": { "$ref": "#/components/schemas/SelfserveWashSummary" } } }, "MachineButtonPressWebhookResponse": { "type": "object", "properties": { "message": { "type": "string" }, "scanner": { "type": "string" }, "lane_id": { "type": "integer" }, "selfserve": { "$ref": "#/components/schemas/SelfserveWashSummary" } } }, "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" }, "machine_type_id": { "type": "integer", "description": "Reusable machine type ID", "nullable": true }, "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)" }, "services": { "type": "array", "description": "Services enabled by this task. Each item must be a valid service enum name.", "items": { "$ref": "#/components/schemas/SelfserveLaneService" }, "default": [] }, "buttons": { "type": "array", "description": "Dynamic image button IDs enabled by this task.", "items": { "type": "integer" }, "default": [] }, "dynamic_images_vehicle_type": { "type": "integer", "nullable": true, "description": "Optional vehicle type selection override for the machine UI." }, "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" }, "machine_type_id": { "type": "integer", "description": "Reusable machine type ID", "nullable": true }, "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" } } }, "DepartmentGuest": { "type": "object", "properties": { "id": { "type": "integer" }, "name": { "type": "string" }, "longitude": { "type": "number", "format": "float" }, "latitude": { "type": "number", "format": "float" }, "address": { "type": "string", "description": "Department address (same as description)" }, "self_serve_enabled": { "type": "boolean" }, "lanes": { "type": "array", "items": { "$ref": "#/components/schemas/DepartmentLaneGuest" } } } }, "DepartmentLaneGuest": { "type": "object", "properties": { "id": { "type": "integer" }, "name": { "type": "string" }, "status": { "type": "string" }, "products": { "type": "array", "items": { "type": "integer" } }, "machine_available": { "type": "boolean" } } }, "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" }, "dynamic_image_id": { "type": "integer", "nullable": true, "minimum": 1 }, "machine_type_id": { "type": "integer", "nullable": true }, "status": { "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" }, "dynamic_image_id": { "type": "integer", "nullable": true, "minimum": 1 }, "machine_type_id": { "type": "integer", "nullable": true } } }, "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" }, "dynamic_image_id": { "type": "integer", "nullable": true, "minimum": 1 }, "machine_type_id": { "type": "integer", "nullable": true } } }, "DepartmentGate": { "type": "object", "properties": { "id": { "type": "integer" }, "department": { "type": "integer" }, "is_entrance": { "type": "boolean" }, "is_exit": { "type": "boolean" }, "name": { "type": "string" }, "config": { "$ref": "#/components/schemas/DepartmentGateConfig" }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" } } }, "DepartmentGateConfig": { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "example": "PHONE_CALL" }, "phone_number": { "type": "string", "nullable": true, "example": 4512345678 }, "call_duration_threshold": { "type": "integer", "nullable": true, "example": 10 } }, "description": "Configuration for the department gate.\nIf type is 'PHONE_CALL', 'phone_number' and 'call_duration_threshold' are required.\n" }, "DepartmentGateCreate": { "type": "object", "required": [ "department", "is_entrance", "is_exit", "name", "config" ], "properties": { "department": { "type": "integer" }, "is_entrance": { "type": "boolean" }, "is_exit": { "type": "boolean" }, "name": { "type": "string" }, "config": { "$ref": "#/components/schemas/DepartmentGateConfig" } } }, "DepartmentGateUpdate": { "type": "object", "required": [ "id" ], "properties": { "id": { "type": "integer" }, "is_entrance": { "type": "boolean" }, "is_exit": { "type": "boolean" }, "name": { "type": "string" }, "config": { "$ref": "#/components/schemas/DepartmentGateConfig" } } }, "DepartmentRelay": { "type": "object", "properties": { "id": { "type": "integer" }, "department": { "type": "integer" }, "relay_id": { "type": "string" }, "name": { "type": "string" }, "type": { "type": "string", "enum": [ "SWITCH", "TRIGGER" ] }, "config": { "$ref": "#/components/schemas/DepartmentRelayConfig" }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" } } }, "DepartmentRelayConfig": { "type": "object", "properties": { "what_happens": { "type": "string", "nullable": true, "example": "open_gate" }, "webhook_token": { "type": "string", "nullable": true, "example": "secret_token" } }, "description": "Configuration for the department relay.\nIf the relay 'type' is 'TRIGGER', 'what_happens' and 'webhook_token' are required.\n" }, "DepartmentRelayCreate": { "type": "object", "required": [ "department", "relay_id", "name", "type", "config" ], "properties": { "department": { "type": "integer" }, "relay_id": { "type": "string" }, "name": { "type": "string" }, "type": { "type": "string", "enum": [ "SWITCH", "TRIGGER" ] }, "config": { "$ref": "#/components/schemas/DepartmentRelayConfig" } } }, "DepartmentRelayUpdate": { "type": "object", "required": [ "id" ], "properties": { "id": { "type": "integer" }, "relay_id": { "type": "string" }, "name": { "type": "string" }, "type": { "type": "string", "enum": [ "SWITCH", "TRIGGER" ] }, "config": { "$ref": "#/components/schemas/DepartmentRelayConfig" } } }, "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" }, "user_id": { "type": "integer", "description": "Internal user ID owning the customer account" }, "reg": { "type": "string", "description": "Vehicle registration number" }, "customer_id": { "type": "integer" }, "customer_name": { "type": "string" }, "type": { "type": "integer", "description": "Product ID representing the vehicle wash type" }, "reference": { "type": "string", "nullable": true, "description": "Optional external reference/label" }, "wash_subscription": { "type": "boolean" }, "barred": { "type": "boolean", "description": "True if the associated customer is barred" }, "addons": { "type": "object", "properties": { "enabled": { "type": "integer" }, "available": { "type": "integer" }, "list": { "type": "array", "items": { "type": "object" } } } }, "last_order_id": { "type": "integer", "nullable": true }, "xlvask": { "type": "object", "nullable": true, "description": "XL Vask vehicle data when available" }, "vehicle_types": { "type": "array", "items": { "type": "object" } }, "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", "enum": [ "PRODUCT", "REVENUE", "VISITS", "NONE" ], "example": "PRODUCT" }, "target": { "type": "number", "description": "Target value for the goal", "example": 100 }, "label": { "type": "string", "description": "Optional short label/title for this goal criteria (max 255 characters)", "example": "Q1 Revenue Goal" }, "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" } }, "progress_alert_frequency": { "type": "string", "description": "Frequency of progress alerts for the goal.\nAccepted values: DAILY, WEEKLY, MONTHLY, CHANGED, NONE.\nThe canonical field name is snake_case `progress_alert_frequency`.\nFor backward-compatibility the API also accepts camelCase `progressAlertFrequency` on input.\n", "enum": [ "DAILY", "WEEKLY", "MONTHLY", "CHANGED", "NONE" ], "example": "DAILY" }, "progress_alert_destination": { "type": "string", "description": "Destination/channel where progress alerts should be delivered.\nAccepted values: SLACK, EMAIL, SMS, NONE.\nThe canonical field name is snake_case `progress_alert_destination`.\nFor backward-compatibility the API also accepts camelCase `progressAlertDestination` on input.\n", "enum": [ "SLACK", "EMAIL", "SMS", "NONE" ], "example": "NONE" }, "progress_alert_progress_type": { "type": "string", "description": "What part of the progress should be included in alert messages.\nAccepted values: ALL, PERCENTAGE_ONLY, COUNT_ONLY, COUNT_AND_TARGET, NONE.\nThe canonical field name is snake_case `progress_alert_progress_type`.\nFor backward-compatibility the API also accepts camelCase `progressAlertProgressType` on input.\n", "enum": [ "ALL", "PERCENTAGE_ONLY", "COUNT_ONLY", "COUNT_AND_TARGET", "NONE" ], "example": "ALL" }, "progress_alert_style": { "type": "string", "description": "Presentation style of the alert.\nAccepted values: DEPARTMENT_COMPARE, COLLECTIVE, SINGLE_DEPARTMENT, NONE.\nThe canonical field name is snake_case `progress_alert_style`.\nFor backward-compatibility the API also accepts camelCase `progressAlertStyle` on input.\n", "enum": [ "DEPARTMENT_COMPARE", "COLLECTIVE", "SINGLE_DEPARTMENT", "NONE" ], "example": "NONE" }, "progress_alert_format": { "type": "string", "nullable": true, "description": "Optional custom template for the alert body. Supports tokens `{label}`, `{percent}`, `{count}`, `{target}`, `{timeframe}`, `{departments}`, `{prefix}`, `{body}`.\nMax length depends on destination: 160 characters for SMS; 1024 characters for EMAIL/SLACK/other.\nThe canonical field name is snake_case `progress_alert_format`.\nFor backward-compatibility the API also accepts camelCase `progressAlertFormat` on input.\n" }, "progress_alert_weekdays": { "type": "array", "description": "Weekdays on which progress alerts should be sent.\nUse one or more of: MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY.\nThe canonical field name is snake_case `progress_alert_weekdays`.\nFor backward-compatibility the API also accepts camelCase `progressAlertWeekdays` on input.\n", "items": { "type": "string", "enum": [ "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY" ] }, "example": [ "MONDAY", "WEDNESDAY", "FRIDAY" ] }, "progress_alert_time_of_day": { "type": "string", "nullable": true, "description": "Time of day (with timezone) when progress alerts should be sent.\nFormat: `HH:MMZ` or `HH:MM±HH:MM` (24-hour clock with UTC offset). Examples: `14:30Z`, `09:15+02:00`, `18:45-05:00`.\nThe canonical field name is snake_case `progress_alert_time_of_day`.\nFor backward-compatibility the API also accepts camelCase `progressAlertTimeOfDay` on input.\n", "pattern": "^([01]\\d|2[0-3]):[0-5]\\d(?:Z|[+-](?:[01]\\d|2[0-3]):?[0-5]\\d)$", "example": "14:30+02:00" }, "department_daily_targets": { "type": "object", "description": "Optional per-department custom daily targets. Keys are department IDs and values are non-negative numbers representing the target per operating day for that department.\nIf omitted, the daily target is split evenly across selected departments. The canonical field name is snake_case `department_daily_targets`.\nFor backward-compatibility the API also accepts camelCase `departmentDailyTargets` on input.\n", "x-additionalPropertiesName": "department_id", "additionalProperties": { "type": "number", "minimum": 0 }, "example": { "12": 3, "15": 5 } } } }, "GoalProgressDetails": { "type": "object", "properties": { "count": { "type": "number", "description": "Current progress value" }, "target": { "type": "number", "description": "Target value for the period" }, "date_from": { "type": "string", "nullable": true, "description": "Inclusive period start datetime (ISO-8601), null when no lower bound applies", "example": "2026-01-01T00:00:00+00:00" }, "date_end": { "type": "string", "nullable": true, "description": "Inclusive period end datetime (ISO-8601), null when no upper bound applies", "example": "2026-02-26T23:59:59+00:00" } } }, "DepartmentGoalProgress": { "type": "object", "title": "Department progress details", "description": "Goal progress details for a single department across multiple timeframes.\nTimeframes are clamped to the goal timeframe (never before goal start and never after goal end).\n", "properties": { "all": { "$ref": "#/components/schemas/GoalProgressDetails" }, "today": { "$ref": "#/components/schemas/GoalProgressDetails" }, "week": { "$ref": "#/components/schemas/GoalProgressDetails" }, "month": { "$ref": "#/components/schemas/GoalProgressDetails" }, "year": { "$ref": "#/components/schemas/GoalProgressDetails" }, "to_date": { "$ref": "#/components/schemas/GoalProgressDetails" } } }, "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" }, "progress": { "type": "object", "description": "Goal progress details for various timeframes.\n`year` starts at January 1 of the current year or the goal start, whichever is later.\n`to_date` starts at the goal start and ends at today (also clamped by goal end).\n", "properties": { "all": { "$ref": "#/components/schemas/GoalProgressDetails" }, "today": { "$ref": "#/components/schemas/GoalProgressDetails" }, "week": { "$ref": "#/components/schemas/GoalProgressDetails" }, "month": { "$ref": "#/components/schemas/GoalProgressDetails" }, "year": { "$ref": "#/components/schemas/GoalProgressDetails" }, "to_date": { "$ref": "#/components/schemas/GoalProgressDetails" }, "departmental_distribution": { "type": "object", "description": "Progress details broken down by department. Keys are department IDs.\nIncludes `all`, `today`, `week`, `month`, `year`, and `to_date` timeframes.\n", "x-additionalPropertiesName": "department_id", "additionalProperties": { "$ref": "#/components/schemas/DepartmentGoalProgress" }, "example": { "12": { "all": { "count": 15, "target": 100 }, "today": { "count": 2, "target": 5 }, "week": { "count": 10, "target": 35 }, "month": { "count": 15, "target": 100 }, "year": { "count": 15, "target": 100 }, "to_date": { "count": 15, "target": 100 } } } } } }, "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" } } }, "Passkey": { "type": "object", "properties": { "id": { "type": "integer" }, "credential_id": { "type": "string", "description": "Base64URL-encoded credential ID" }, "name": { "type": "string", "nullable": true }, "algorithm": { "type": "string", "example": "ES256" }, "transports": { "type": "array", "items": { "type": "string" }, "example": [ "usb", "nfc", "ble", "internal" ] }, "sign_count": { "type": "integer" }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" } } }, "PasskeyCreateRequest": { "type": "object", "required": [ "credential_id", "public_key", "algorithm", "transports" ], "properties": { "credential_id": { "type": "string", "description": "Base64URL-encoded credential ID returned from WebAuthn" }, "public_key": { "type": "string", "description": "Base64URL-encoded public key (COSE or PEM as stored)" }, "algorithm": { "type": "string", "example": "ES256" }, "transports": { "type": "array", "items": { "type": "string" } }, "name": { "type": "string", "nullable": true } } }, "PasskeyRenameRequest": { "type": "object", "required": [ "name" ], "properties": { "name": { "type": "string" } } }, "BirdVoiceCall": { "type": "object", "properties": { "id": { "type": "string", "example": "4015cf84-8028-46a1-a0d9-9213e5bf4f09" }, "channelId": { "type": "string", "example": "a2545e48-fe8c-5741-9bdc-42a081076bc9" }, "originator": { "type": "object", "properties": { "number": { "type": "object", "properties": { "type": { "type": "string", "example": "pstn" }, "number": { "type": "string", "example": "+4532330288" }, "countryIsoCode": { "type": "string", "example": "DK" } } } } }, "receiver": { "type": "object", "properties": { "contact": { "type": "object", "properties": { "id": { "type": "string", "example": "18229adf-af8c-404a-b036-ae193c22e33c" }, "identifierKey": { "type": "string", "example": "phonenumber" }, "identifierValue": { "type": "string", "example": "+4542331128" } } }, "number": { "type": "object", "properties": { "type": { "type": "string", "example": "pstn" }, "number": { "type": "string", "example": "+4542331128" }, "countryIsoCode": { "type": "string", "example": "DK" } } } } }, "from": { "type": "string", "example": "+4532330288" }, "to": { "type": "string", "example": "+4542331128" }, "direction": { "type": "string", "example": "outgoing" }, "status": { "type": "string", "example": "completed" }, "type": { "type": "string", "example": "pstn" }, "duration": { "type": "integer", "example": 3 }, "hangupCauseCode": { "type": "integer", "example": 16 }, "hangupSource": { "type": "string", "example": "callee" }, "sipInsights": { "type": "object", "properties": { "hangupSipCode": { "type": "string", "example": "200" } } }, "qualityInsights": { "type": "object", "properties": { "mos": { "type": "string", "example": "4.50" }, "pdd": { "type": "string", "example": "2.15" } } }, "price": { "type": "object", "properties": { "currencyCode": { "type": "string", "example": "EUR" }, "amount": { "type": "string", "example": "0.0079" } } }, "createdAt": { "type": "string", "format": "date-time", "example": "2026-02-27T13:45:36.216Z" }, "updatedAt": { "type": "string", "format": "date-time", "example": "2026-02-27T13:45:46.532Z" }, "ringingAt": { "type": "string", "format": "date-time", "example": "2026-02-27T13:45:38.367Z" }, "answeredAt": { "type": "string", "format": "date-time", "example": "2026-02-27T13:45:43.359Z" }, "endedAt": { "type": "string", "format": "date-time", "example": "2026-02-27T13:45:46.368Z" } } }, "BirdVoiceCallListResponse": { "type": "object", "properties": { "success": { "type": "boolean", "example": true }, "data": { "type": "object", "properties": { "nextPageToken": { "type": "string", "example": "WzE3NzIxNTY5NTI0MDUsIjk5ZDU4M2VkLTQyMzAtNDExNy1hOTQ0LTllY2JjNzhmYWJlMSJd" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/BirdVoiceCall" } } } }, "meta": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ], "example": [] }, "includes": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ], "example": [] } } }, "BirdVoiceCallSingleResponse": { "type": "object", "properties": { "success": { "type": "boolean", "example": true }, "data": { "$ref": "#/components/schemas/BirdVoiceCall" }, "meta": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ], "example": [] }, "includes": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ], "example": [] } } }, "BirdTestOutboundCallResponse": { "type": "object", "properties": { "success": { "type": "boolean", "example": true }, "data": { "type": "object", "properties": { "to": { "type": "string", "example": "+45 42 33 11 28" }, "to_e164": { "type": "string", "example": "+4542331128" }, "call_id": { "type": "string", "nullable": true, "example": "4015cf84-8028-46a1-a0d9-9213e5bf4f09" }, "final_status": { "type": "string", "nullable": true, "example": "completed" }, "hangup_sent": { "type": "boolean", "example": true }, "created_call": { "$ref": "#/components/schemas/BirdVoiceCall" }, "last_call_snapshot": { "$ref": "#/components/schemas/BirdVoiceCall" }, "hangup_response": { "$ref": "#/components/schemas/BirdVoiceCall" } } }, "meta": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ], "example": [] }, "includes": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ], "example": [] } } }, "BirdNumber": { "type": "object", "properties": { "id": { "type": "string", "example": "019c73dc-60f1-76c4-98b0-c4318706b938" }, "workspaceId": { "type": "string", "example": "3d5fae4f-9c2d-41aa-9840-28b18e6a94bc" }, "type": { "type": "string", "example": "national" }, "country": { "type": "string", "example": "DK" }, "number": { "type": "string", "example": "+4532330288" }, "status": { "type": "string", "example": "active" }, "capabilities": { "type": "object", "properties": { "voice": { "$ref": "#/components/schemas/BirdNumberCapability" }, "sms": { "$ref": "#/components/schemas/BirdNumberCapability" }, "mms": { "$ref": "#/components/schemas/BirdNumberCapability" }, "fax": { "$ref": "#/components/schemas/BirdNumberCapability" }, "whatsapp": { "$ref": "#/components/schemas/BirdNumberCapability" } } }, "monthlyRecurringPrice": { "type": "object", "properties": { "currencyCode": { "type": "string", "example": "EUR" }, "amount": { "type": "integer", "example": 1000000 }, "exponent": { "type": "integer", "example": -6 } } }, "complianceRequirements": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "configurations": { "type": "object", "additionalProperties": true }, "createdAt": { "type": "string", "format": "date-time", "example": "2026-02-19T03:05:48.529Z" }, "updatedAt": { "type": "string", "format": "date-time", "example": "2026-02-19T03:08:17.753Z" }, "activatedAt": { "type": "string", "format": "date-time", "nullable": true, "example": "2026-02-19T03:05:48.529Z" }, "deactivatedAt": { "type": "string", "format": "date-time", "nullable": true }, "deactivatesAt": { "type": "string", "format": "date-time", "nullable": true }, "subscription": { "type": "object", "additionalProperties": true }, "endpointSubscription": { "type": "object", "additionalProperties": true }, "requirements": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "whatsApp": { "type": "object", "additionalProperties": true }, "endpoint": { "type": "object", "additionalProperties": true } } }, "BirdNumberCapability": { "type": "object", "properties": { "inbound": { "type": "boolean" }, "outbound": { "type": "boolean" } } }, "BirdNumberListResponse": { "type": "object", "properties": { "success": { "type": "boolean", "example": true }, "data": { "type": "object", "properties": { "results": { "type": "array", "items": { "$ref": "#/components/schemas/BirdNumber" } } } }, "meta": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ], "example": [] }, "includes": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ], "example": [] } } }, "BirdNumberSingleResponse": { "type": "object", "properties": { "success": { "type": "boolean", "example": true }, "data": { "$ref": "#/components/schemas/BirdNumber" }, "meta": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ], "example": [] }, "includes": { "oneOf": [ { "type": "array", "items": {} }, { "type": "object", "additionalProperties": true } ], "example": [] } } } } } }