Files
api/openapi.yaml
T

10073 lines
286 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
openapi: 3.0.3
info:
title: Copenhagen Truck Wash API
description: |
This API provides access to the Copenhagen Truck Wash system, managing orders, bookings,
departments, products, customers, and various integrations including e-conomic, Stripe,
XLVask, and more.
## Authentication
Most endpoints require authentication using a Bearer token obtained from the `/auth/login`
or `/auth/employee/login` endpoints.
## Permissions
Many endpoints require specific permissions that are assigned to user groups/roles.
## Subusers and customer targeting
When authenticated as a subuser, most customer-scoped endpoints require an explicit target
customer context. Provide the header `X-Customer-Number: <customer_number>` to target a
specific customer. If omitted, the API attempts to infer the customer from the authenticated
user context when possible. Classic user sessions ignore this header.
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
description: Local development server
security:
- BearerAuth: []
tags:
- name: Authentication
description: User and employee authentication endpoints
- name: Users
description: User management and customer operations
- name: Orders
description: Order creation, management, and retrieval
- name: Order Items
description: Managing items within orders
- name: Bookings
description: Booking management for wash services
- name: Departments
description: Department and location management
- name: Products
description: Product catalog and pricing
- name: Categories
description: Product category management
- name: Invoices
description: Invoice generation and management
- name: Payments
description: Payment processing and collection
- name: Vehicles
description: Vehicle registration and management
- name: Notifications
description: System notifications and alerts
- name: Statistics
description: Business analytics and reporting
- name: Modules
description: Third-party integrations and modules
- name: Attachments
description: File upload and attachment management
- name: Forms
description: Form submissions and management
- name: Worker
description: System worker status and maintenance
- name: Plate Scans
description: License plate scanning operations
- name: Config
description: Module configuration management
- name: Branding
description: Branding options management
- name: Roles
description: Role and permission management
- name: Self-Serve
description: Self-serve lane operations and questions
- name: Goals
description: Department goals management
- name: Subusers
description: Subuser registration and setup
- name: Bird
description: Voice Calls via Bird
paths:
# Bird Voice Calls
/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
/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 Calling
/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 (public registration + setup)
/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
authenticated user's customer number. Only subusers with at least one enabled, non-deleted
grant for the caller's customer are returned.
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
`billing_customer_number`.
Notes:
- This endpoint is available only to authenticated subuser sessions.
- It does not require the `X-Customer-Number` header; all enabled, non-deleted grants for the
subuser are included in the response.
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
CVR via e-conomic, ensures the phone number is not already in use, and if SMS is enabled
sends a setup link by SMS for the user to complete registration.
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 (13 digits)
example: 45
phone:
type: integer
description: Phone number (415 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
for their customer number to this subuser. Otherwise returns 404.
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
`username` and `email`.
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
identifiers: `phone_country_code` + `phone`, `subuser_id`, or `username`.
On success, returns a newly generated session token for the subuser.
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 (13 digits)
minimum: 1
maximum: 999
example: 45
phone:
type: integer
description: Phone number (415 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' }
# Authentication Endpoints
/auth/login:
post:
tags:
- Authentication
summary: Customer login
description: Authenticate a customer using customer number and password
operationId: customerLogin
security: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- customer_number
- password
- 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
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
'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
'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
'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
/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
/su/intimidate:
post:
tags:
- Authentication
summary: Intimidate a user
description: Create an authentication token for another user (Superuser only)
operationId: suIntimidate
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [user_id]
properties:
user_id: {type: integer}
responses:
'200':
description: Success
content:
application/json:
schema:
type: object
properties:
token: {type: string}
# User Endpoints
/users:
get:
tags:
- Users
summary: List users
description: Retrieve a paginated list of users
operationId: listUsers
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
- $ref: '#/components/parameters/SearchParam'
responses:
'200':
description: Users retrieved successfully
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
post:
tags:
- Users
summary: Create new user
description: Create a new user account
operationId: createUser
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserCreate'
responses:
'201':
description: User created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
put:
tags:
- Users
summary: Update user
description: Update an existing user
operationId: updateUser
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserUpdate'
responses:
'200':
description: User updated successfully
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/users/customer:
get:
tags:
- Users
summary: Get customer details
description: Get details about a specific customer
operationId: getCustomer
parameters:
- name: customer_number
in: query
schema:
type: integer
responses:
'200':
description: Customer retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
$ref: '#/components/responses/NotFound'
/superuser/user:
get:
tags:
- Users
summary: Get user by ID (superuser)
description: Get detailed user information by user ID
operationId: getSuperuserUser
parameters:
- name: user_id
in: query
schema:
type: integer
responses:
'200':
description: User retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
$ref: '#/components/responses/NotFound'
/admin/customer/code:
get:
tags:
- Users
summary: Get customer code
operationId: getCustomerCode
parameters:
- name: customer_number
in: query
schema: {type: integer}
- name: user_id
in: query
schema: {type: integer}
responses:
'200':
description: Success
post:
tags:
- Users
summary: Add customer code
operationId: addCustomerCode
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
customer_number: {type: integer}
user_id: {type: integer}
code: {type: string}
responses:
'200':
description: Success
/customer/department/default:
get:
tags:
- Users
summary: Get customer default department
operationId: getCustomerDefaultDepartment
parameters:
- name: customer_number
in: query
schema: {type: integer}
responses:
'200':
description: Success
post:
tags:
- Users
summary: Add customer default department
operationId: addCustomerDefaultDepartment
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [department]
properties:
customer_number: {type: integer}
department: {type: integer}
responses:
'200':
description: Success
delete:
tags:
- Users
summary: Delete customer default department
operationId: deleteCustomerDefaultDepartment
parameters:
- name: customer_number
in: query
schema: {type: integer}
responses:
'200':
description: Success
/customer/pricing/fixed:
get:
tags:
- Users
summary: Get customer fixed pricing
operationId: getCustomerFixedPricing
parameters:
- name: customer_number
in: query
required: true
schema: {type: integer}
responses:
'200':
description: Success
post:
tags:
- Users
summary: Add customer fixed pricing
operationId: addCustomerFixedPricing
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [customer_number, price, description]
properties:
customer_number: {type: integer}
price: {type: integer}
description: {type: string}
responses:
'200':
description: Success
delete:
tags:
- Users
summary: Delete customer fixed pricing
operationId: deleteCustomerFixedPricing
parameters:
- name: customer_number
in: query
required: true
schema: {type: integer}
responses:
'200':
description: Success
/account/notifications:
put:
tags:
- Users
summary: Update user notification settings
operationId: updateUserNotifications
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
wash_certificate_email: {type: string}
sms_notifications_enabled: {type: boolean}
email_notifications_enabled: {type: boolean}
responses:
'200':
description: Success
/user/permissions:
get:
tags:
- Users
summary: Get user permissions
operationId: getUserPermissions
responses:
'200':
description: Success
/customers:
get:
tags:
- Users
summary: List customers
operationId: listCustomers
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
- $ref: '#/components/parameters/SearchParam'
responses:
'200':
description: Success
/superuser/user/discounts:
get:
tags:
- Users
summary: Get user discounts
operationId: getUserDiscounts
parameters:
- name: user_id
in: query
required: true
schema: {type: integer}
responses:
'200':
description: Success
post:
tags:
- Users
summary: Set user discount
operationId: setUserDiscount
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [discount, object_id, is_category]
properties:
user_id: {type: integer}
discount: {type: integer}
object_id: {type: string}
is_category: {type: boolean}
responses:
'200':
description: Success
/superuser/user/keys:
get:
tags:
- Users
summary: Get user keys
operationId: getUserKeys
parameters:
- name: user_id
in: query
required: true
schema: {type: integer}
- name: key
in: query
schema: {type: string}
responses:
'200':
description: Success
post:
tags:
- Users
summary: Set user key
operationId: setUserKey
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [key, value]
properties:
user_id: {type: integer}
key: {type: string}
value: {type: string}
responses:
'200':
description: Success
/superuser/user/password:
post:
tags:
- Users
summary: Set user password
operationId: setUserPassword
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [password]
properties:
user_id: {type: integer}
password: {type: string}
responses:
'200':
description: Success
/admin/customer/getUserId:
get:
tags:
- Users
summary: Get user ID from customer number
description: Convert e-conomic customer number to internal user ID
operationId: getUserIdFromCustomerNumber
parameters:
- name: customer_number
in: query
required: true
schema:
type: integer
responses:
'200':
description: User ID retrieved successfully
content:
application/json:
schema:
type: object
properties:
user_id:
type: integer
/admin/customer/name:
get:
tags:
- Users
summary: Get customer name
description: Get the full name of a customer
operationId: getCustomerName
parameters:
- name: user_id
in: query
schema:
type: integer
responses:
'200':
description: Customer name retrieved successfully
content:
application/json:
schema:
type: object
properties:
name:
type: string
# Orders Endpoints
/orders:
get:
tags:
- Orders
summary: List orders
description: Retrieve a paginated list of orders
operationId: listOrders
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
- $ref: '#/components/parameters/SearchParam'
- name: show_wash_subscription
in: query
schema:
type: string
enum: [true, false]
responses:
'200':
description: Orders retrieved successfully
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Order'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
post:
tags:
- Orders
summary: Create new order
description: Create a new wash order
operationId: createOrder
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/OrderCreate'
responses:
'201':
description: Order created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
delete:
tags:
- Orders
summary: Delete order
description: Delete an existing order
operationId: deleteOrder
parameters:
- name: id
in: query
required: true
schema:
type: integer
responses:
'200':
description: Order deleted successfully
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
put:
tags:
- Orders
summary: Update order (alias)
description: Update an existing order
operationId: updateOrders
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/OrderUpdate'
responses:
'200':
description: Order updated successfully
'400':
$ref: '#/components/responses/BadRequest'
/order:
get:
tags:
- Orders
summary: Get order details
description: Get detailed information about a specific order
operationId: getOrder
parameters:
- name: id
in: query
required: true
schema:
type: integer
responses:
'200':
description: Order retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
'404':
$ref: '#/components/responses/NotFound'
put:
tags:
- Orders
summary: Update order
description: Update an existing order
operationId: updateOrder
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/OrderUpdate'
responses:
'200':
description: Order updated successfully
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/user/orders:
get:
tags:
- Orders
summary: Get current user's orders
description: Retrieve orders for the authenticated user
operationId: getUserOrders
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
responses:
'200':
description: Orders retrieved successfully
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Order'
/user/order:
get:
tags:
- Orders
summary: Get user's specific order
description: Get details of a specific order for the authenticated user
operationId: getUserOrder
parameters:
- name: id
in: query
required: true
schema:
type: integer
responses:
'200':
description: Order retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
/orders/mark_as_completed:
post:
tags:
- Orders
summary: Mark order as completed
description: Mark an order as completed
operationId: markOrderCompleted
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- id
properties:
id:
type: integer
responses:
'200':
description: Order marked as completed successfully
'400':
$ref: '#/components/responses/BadRequest'
/order/wash-certificate:
post:
tags:
- Orders
summary: Generate wash certificate
description: Generate a wash certificate for an order
operationId: generateWashCertificate
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
order_id:
type: integer
responses:
'200':
description: Wash certificate generated successfully
'400':
$ref: '#/components/responses/BadRequest'
# Order Items Endpoints
/order/items:
get:
tags:
- Order Items
summary: List order items
description: Get all items for a specific order
operationId: listOrderItems
parameters:
- name: order_id
in: query
required: true
schema:
type: integer
responses:
'200':
description: Order items retrieved successfully
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/OrderItem'
post:
tags:
- Order Items
summary: Add item to order
description: Add a new item to an existing order
operationId: addOrderItem
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/OrderItemCreate'
responses:
'201':
description: Order item added successfully
'400':
$ref: '#/components/responses/BadRequest'
put:
tags:
- Order Items
summary: Update order item
description: Update an existing order item
operationId: updateOrderItem
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/OrderItemUpdate'
responses:
'200':
description: Order item updated successfully
'400':
$ref: '#/components/responses/BadRequest'
delete:
tags:
- Order Items
summary: Delete order item
description: Remove an item from an order
operationId: deleteOrderItem
parameters:
- name: id
in: query
required: true
schema:
type: integer
responses:
'200':
description: Order item deleted successfully
'404':
$ref: '#/components/responses/NotFound'
# Departments Endpoints
/departments:
get:
tags:
- Departments
summary: List departments
description: Retrieve a list of all visible departments
operationId: listDepartments
parameters:
- name: id
in: query
schema:
type: integer
description: Filter by specific department ID
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
- $ref: '#/components/parameters/SearchParam'
responses:
'200':
description: Departments retrieved successfully
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Department'
'401':
$ref: '#/components/responses/Unauthorized'
post:
tags:
- Departments
summary: Create department
description: Create a new department
operationId: createDepartment
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentCreate'
responses:
'201':
description: Department created successfully
'400':
$ref: '#/components/responses/BadRequest'
put:
tags:
- Departments
summary: Update department
description: Update an existing department
operationId: updateDepartment
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentUpdate'
responses:
'200':
description: Department updated successfully
'400':
$ref: '#/components/responses/BadRequest'
/departments/categories:
get:
tags:
- Departments
summary: Get department categories
description: Get product categories available in a department
operationId: getDepartmentCategories
parameters:
- name: department_id
in: query
required: true
schema:
type: integer
responses:
'200':
description: Department categories retrieved successfully
post:
tags:
- Departments
summary: Add category to department
description: Associate a product category with a department
operationId: addDepartmentCategory
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
department_id:
type: integer
category_id:
type: integer
responses:
'201':
description: Category added to department successfully
delete:
tags:
- Departments
summary: Remove category from department
operationId: removeDepartmentCategory
responses:
'200': {description: Success}
/departments/self-serve/enabled:
get:
tags:
- Departments
summary: Get department self-serve status
description: Check if self-serve is enabled for a specific department
operationId: getDepartmentSelfServeEnabled
parameters:
- name: id
in: query
required: true
description: Department ID
schema:
type: integer
responses:
'200':
description: Successfully retrieved status
content:
application/json:
schema:
type: object
properties:
enabled:
type: boolean
'404':
$ref: '#/components/responses/NotFound'
put:
tags:
- Departments
summary: Update department self-serve status
description: Enable or disable self-serve for a specific department
operationId: updateDepartmentSelfServeEnabled
parameters:
- name: id
in: query
required: true
description: Department ID
schema:
type: integer
- name: enabled
in: query
required: true
description: Enabled status (true/false)
schema:
type: string
enum: ['true', 'false']
responses:
'200':
description: Status updated successfully
content:
application/json:
schema:
type: object
properties:
message:
type: string
'404':
$ref: '#/components/responses/NotFound'
/departments/order/recommended:
get:
tags:
- Departments
summary: Get recommended order for department
operationId: getDepartmentRecommendedOrder
parameters:
- name: department_id
in: query
required: true
schema: {type: integer}
responses:
'200': {description: Success}
/department/lanes:
get:
tags:
- Departments
summary: List department lanes
description: Retrieve a list of all department lanes
operationId: listDepartmentLanes
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
- $ref: '#/components/parameters/SearchParam'
responses:
'200':
description: Department lanes retrieved successfully
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/DepartmentLane'
'401':
$ref: '#/components/responses/Unauthorized'
post:
tags:
- Departments
summary: Create department lane
description: Create a new department lane
operationId: createDepartmentLane
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentLaneCreate'
responses:
'201':
description: Department lane created successfully
'400':
$ref: '#/components/responses/BadRequest'
put:
tags:
- Departments
summary: Update department lane
description: Update an existing department lane
operationId: updateDepartmentLane
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentLaneUpdate'
responses:
'200':
description: Department lane updated successfully
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
/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
/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
/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.
You can optionally highlight button indices, set the current step indicator, and toggle only-current-step mode.
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
'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/questions:
get:
tags:
- Self-Serve
summary: List self-serve questions
description: Retrieve a list of self-serve questions for a department, lane, or product.
operationId: listSelfserveQuestions
parameters:
- name: id
in: query
description: Filter by question ID
schema:
type: integer
- name: department
in: query
description: Filter by department ID
schema:
type: integer
- name: lane
in: query
description: Filter by lane ID
schema:
type: integer
- name: product
in: query
description: Filter by product ID
schema:
type: integer
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
- $ref: '#/components/parameters/SearchParam'
- $ref: '#/components/parameters/FiltersParam'
responses:
'200':
description: Successfully retrieved questions
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/DepartmentSelfserveQuestion'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
post:
tags:
- Self-Serve
summary: Add self-serve question
description: Add a new self-serve question.
operationId: addSelfserveQuestion
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- department
- lane
- product
- question
- description
properties:
department:
type: integer
lane:
type: integer
product:
type: integer
question:
type: string
description:
type: string
condition_id:
type: integer
nullable: true
order_priority:
type: integer
default: 0
responses:
'200':
description: Successfully added question
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentSelfserveQuestion'
'400':
$ref: '#/components/responses/BadRequest'
'500':
$ref: '#/components/responses/InternalServerError'
put:
tags:
- Self-Serve
summary: Update self-serve question
description: Update an existing self-serve question.
operationId: updateSelfserveQuestion
parameters:
- name: id
in: query
required: true
description: Question ID
schema:
type: integer
requestBody:
content:
application/json:
schema:
type: object
properties:
department:
type: integer
lane:
type: integer
product:
type: integer
question:
type: string
description:
type: string
condition_id:
type: integer
nullable: true
order_priority:
type: integer
responses:
'200':
description: Successfully updated question
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentSelfserveQuestion'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
delete:
tags:
- Self-Serve
summary: Delete self-serve question
description: Delete a self-serve question by ID.
operationId: deleteSelfserveQuestion
parameters:
- name: id
in: query
required: true
description: Question ID
schema:
type: integer
responses:
'200':
description: Successfully deleted question
content:
application/json:
schema:
type: string
example: Question deleted
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
/department/selfserve/conditions:
get:
tags:
- Self-Serve
summary: List self-serve conditions
description: Retrieve a list of self-serve conditions for a department, lane, or product.
operationId: listSelfserveConditions
parameters:
- name: id
in: query
description: Filter by condition ID
schema:
type: integer
- name: department
in: query
description: Filter by department ID
schema:
type: integer
- name: lane
in: query
description: Filter by lane ID
schema:
type: integer
- name: product
in: query
description: Filter by product ID
schema:
type: integer
- name: condition_id
in: query
description: Filter by condition ID
schema:
type: integer
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
- $ref: '#/components/parameters/SearchParam'
- $ref: '#/components/parameters/FiltersParam'
responses:
'200':
description: Successfully retrieved conditions
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/DepartmentSelfserveCondition'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
post:
tags:
- Self-Serve
summary: Add self-serve condition
description: Add a new self-serve condition.
operationId: addSelfserveCondition
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- department
- lane
- product
- name
- description
properties:
department:
type: integer
lane:
type: integer
product:
type: integer
condition_id:
type: integer
nullable: true
name:
type: string
description:
type: string
responses:
'200':
description: Successfully added condition
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentSelfserveCondition'
'400':
$ref: '#/components/responses/BadRequest'
'500':
$ref: '#/components/responses/InternalServerError'
put:
tags:
- Self-Serve
summary: Update self-serve condition
description: Update an existing self-serve condition.
operationId: updateSelfserveCondition
parameters:
- name: id
in: query
required: true
description: Condition ID
schema:
type: integer
requestBody:
content:
application/json:
schema:
type: object
properties:
department:
type: integer
lane:
type: integer
product:
type: integer
condition_id:
type: integer
nullable: true
name:
type: string
description:
type: string
responses:
'200':
description: Successfully updated condition
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentSelfserveCondition'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
delete:
tags:
- Self-Serve
summary: Delete self-serve condition
description: Delete a self-serve condition.
operationId: deleteSelfserveCondition
parameters:
- name: id
in: query
required: true
description: Condition ID
schema:
type: integer
responses:
'200':
description: Successfully deleted condition
content:
application/json:
schema:
type: string
example: Condition deleted
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
/department/selfserve/condition/rules:
get:
tags:
- Self-Serve
summary: List self-serve condition rules
description: Retrieve a list of self-serve condition rules.
operationId: listSelfserveConditionRules
parameters:
- name: id
in: query
description: Filter by rule ID
schema:
type: integer
- name: condition_id
in: query
description: Filter by condition ID
schema:
type: integer
- name: type
in: query
description: Filter by rule type
schema:
type: string
- name: object_type
in: query
description: Filter by object type
schema:
type: string
- name: object_id
in: query
description: Filter by object ID
schema:
type: integer
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
- $ref: '#/components/parameters/SearchParam'
- $ref: '#/components/parameters/FiltersParam'
responses:
'200':
description: Successfully retrieved condition rules
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/DepartmentSelfserveConditionRule'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
post:
tags:
- Self-Serve
summary: Add self-serve condition rule
description: Add a new self-serve condition rule.
operationId: addSelfserveConditionRule
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- condition_id
- type
- object_type
- object_id
- name
- description
properties:
condition_id:
type: integer
type:
type: string
object_type:
type: string
object_id:
type: integer
name:
type: string
description:
type: string
responses:
'200':
description: Successfully added condition rule
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentSelfserveConditionRule'
'400':
$ref: '#/components/responses/BadRequest'
'500':
$ref: '#/components/responses/InternalServerError'
put:
tags:
- Self-Serve
summary: Update self-serve condition rule
description: Update an existing self-serve condition rule.
operationId: updateSelfserveConditionRule
parameters:
- name: id
in: query
required: true
description: Rule ID
schema:
type: integer
requestBody:
content:
application/json:
schema:
type: object
properties:
condition_id:
type: integer
type:
type: string
object_type:
type: string
object_id:
type: integer
name:
type: string
description:
type: string
responses:
'200':
description: Successfully updated condition rule
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentSelfserveConditionRule'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
delete:
tags:
- Self-Serve
summary: Delete self-serve condition rule
description: Delete a self-serve condition rule.
operationId: deleteSelfserveConditionRule
parameters:
- name: id
in: query
required: true
description: Rule ID
schema:
type: integer
responses:
'200':
description: Successfully deleted condition rule
content:
application/json:
schema:
type: string
example: Rule deleted
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
/department/selfserve/vehicle/conditions:
get:
tags:
- Self-Serve
summary: List vehicle conditions
description: Retrieve a list of vehicle conditions for a department, lane, reg, or question. Customers will only see their own vehicle conditions.
operationId: listSelfserveVehicleConditions
parameters:
- name: id
in: query
description: Filter by condition ID
schema:
type: integer
- name: department
in: query
description: Filter by department ID
schema:
type: integer
- name: lane
in: query
description: Filter by lane ID
schema:
type: integer
- name: reg
in: query
description: Filter by vehicle registration number
schema:
type: string
- name: question
in: query
description: Filter by question ID
schema:
type: integer
- name: customer_id
in: query
description: Filter by customer ID
schema:
type: integer
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
- $ref: '#/components/parameters/SearchParam'
- $ref: '#/components/parameters/FiltersParam'
responses:
'200':
description: Successfully retrieved vehicle conditions
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/DepartmentSelfserveVehicleCondition'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
post:
tags:
- Self-Serve
summary: Add vehicle condition
description: Add a new vehicle condition (answer to a question). Customers can only add conditions for their own vehicles.
operationId: addSelfserveVehicleCondition
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- department
- lane
- reg
- question
properties:
department:
type: integer
lane:
type: integer
reg:
type: string
question:
type: integer
value:
type: boolean
customer_id:
type: integer
nullable: true
responses:
'200':
description: Successfully added vehicle condition
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentSelfserveVehicleCondition'
'400':
$ref: '#/components/responses/BadRequest'
'500':
$ref: '#/components/responses/InternalServerError'
put:
tags:
- Self-Serve
summary: Update vehicle condition
description: Update an existing vehicle condition. Customers can only update conditions for their own vehicles.
operationId: updateSelfserveVehicleCondition
parameters:
- name: id
in: query
required: true
description: Condition ID
schema:
type: integer
requestBody:
content:
application/json:
schema:
type: object
properties:
department:
type: integer
lane:
type: integer
reg:
type: string
question:
type: integer
value:
type: boolean
customer_id:
type: integer
nullable: true
responses:
'200':
description: Successfully updated vehicle condition
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentSelfserveVehicleCondition'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
delete:
tags:
- Self-Serve
summary: Delete vehicle condition
description: Delete a vehicle condition. Customers can only delete conditions for their own vehicles.
operationId: deleteSelfserveVehicleCondition
parameters:
- name: id
in: query
required: true
description: Condition ID
schema:
type: integer
responses:
'200':
description: Successfully deleted vehicle condition
content:
application/json:
schema:
type: string
example: Condition deleted
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
/department/selfserve/tasks:
get:
tags:
- Self-Serve
summary: List self-serve tasks
description: Retrieve a list of self-serve tasks for a department, lane, product, or condition_id.
operationId: listSelfserveTasks
parameters:
- name: id
in: query
description: Filter by task ID
schema:
type: integer
- name: department
in: query
description: Filter by department ID
schema:
type: integer
- name: lane
in: query
description: Filter by lane ID
schema:
type: integer
- name: product
in: query
description: Filter by product ID
schema:
type: integer
- name: condition_id
in: query
description: Filter by condition ID
schema:
type: integer
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
- $ref: '#/components/parameters/SearchParam'
- $ref: '#/components/parameters/FiltersParam'
responses:
'200':
description: Successfully retrieved tasks
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/DepartmentSelfserveTask'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
post:
tags:
- Self-Serve
summary: Add self-serve task
description: Add a new self-serve task.
operationId: addSelfserveTask
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- department
- lane
- product
- task
- description
properties:
department:
type: integer
lane:
type: integer
product:
type: integer
condition_id:
type: integer
nullable: true
task:
type: string
description:
type: string
order_priority:
type: integer
default: 0
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
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
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
delete:
tags:
- Self-Serve
summary: Delete task attachment
description: Remove an attachment from a specific self-serve task.
operationId: deleteSelfserveTaskAttachment
parameters:
- name: task_id
in: query
required: true
description: Task ID
schema:
type: integer
- name: attachment_id
in: query
required: true
description: Attachment ID
schema:
type: integer
responses:
'200':
description: Attachment deleted successfully
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Attachment deleted successfully
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
/department/selfserve/tasks/attachments/upload:
post:
tags:
- Self-Serve
summary: Upload task attachment
description: Upload a new attachment to a specific self-serve task using base64 encoding.
operationId: uploadSelfserveTaskAttachment
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- task_id
- base64_file
- file_name
properties:
task_id:
type: integer
base64_file:
type: string
description: Base64 encoded file content
file_name:
type: string
description: Name of the file including extension
responses:
'200':
description: Attachment uploaded successfully
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
/department/selfserve/tasks/attachments/download:
get:
tags:
- Self-Serve
summary: Download task attachment
description: Generate a download link for a specific self-serve task attachment.
operationId: downloadSelfserveTaskAttachment
parameters:
- name: task_id
in: query
required: true
description: Task ID
schema:
type: integer
- name: attachment_id
in: query
required: true
description: Attachment ID
schema:
type: integer
responses:
'200':
description: Successfully generated download link
content:
application/json:
schema:
type: object
properties:
download_link:
type: string
format: uri
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
# Products Endpoints
/products:
get:
tags:
- Products
summary: List products
description: Retrieve a list of products with optional filters for customer pricing and department
operationId: listProducts
parameters:
- name: customer_id
in: query
schema:
type: integer
description: Customer ID for custom pricing
- name: department_id
in: query
schema:
type: integer
description: Department ID for department-specific pricing
- name: category
in: query
schema:
type: integer
description: Filter by category ID
- name: id
in: query
schema:
type: integer
description: Get specific product by ID
- name: final_price
in: query
schema:
type: boolean
description: Whether to return final prices including discounts
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
responses:
'200':
description: Products retrieved successfully
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Product'
post:
tags:
- Products
summary: Create product
description: Create a new product
operationId: createProduct
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ProductCreate'
responses:
'201':
description: Product created successfully
'400':
$ref: '#/components/responses/BadRequest'
put:
tags:
- Products
summary: Update product
description: Update an existing product
operationId: updateProduct
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ProductUpdate'
responses:
'200':
description: Product updated successfully
'400':
$ref: '#/components/responses/BadRequest'
# Categories Endpoints
/categories:
get:
tags:
- Categories
summary: List categories
description: Retrieve a list of product categories
operationId: listCategories
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
responses:
'200':
description: Categories retrieved successfully
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Category'
post:
tags:
- Categories
summary: Create category
description: Create a new product category
operationId: createCategory
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CategoryCreate'
responses:
'201':
description: Category created successfully
put:
tags:
- Categories
summary: Update category
description: Update an existing category
operationId: updateCategory
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CategoryUpdate'
responses:
'200':
description: Category updated successfully
# Bookings Endpoints
/bookings:
get:
tags:
- Bookings
summary: List bookings
description: Retrieve a list of bookings
operationId: listBookings
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
responses:
'200':
description: Bookings retrieved successfully
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Booking'
put:
tags:
- Bookings
summary: Update booking
description: Update an existing booking
operationId: updateBooking
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/BookingUpdate'
responses:
'200':
description: Booking updated successfully
/user/bookings:
get:
tags:
- Bookings
summary: Get user bookings
description: Retrieve bookings for the authenticated user
operationId: getUserBookings
responses:
'200':
description: User bookings retrieved successfully
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Booking'
/order-bookings:
get:
tags:
- Bookings
summary: List order bookings
operationId: listOrderBookings
parameters:
- name: id
in: query
required: false
schema: {type: integer}
responses:
'200':
description: Success
# Goals Endpoints
/goals/department:
get:
tags:
- Goals
summary: List or get department goals
description: |
Retrieve a list of department goals or a single goal when `id` is provided.
Access control:
- A user may only access goals where the goal's `departments` set is a subset of the user's departments.
- Users with the `superuser` permission may access all goals.
operationId: listDepartmentGoals
parameters:
- name: id
in: query
required: false
schema: { type: integer }
description: When provided, returns the single goal with this id (if accessible)
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
- $ref: '#/components/parameters/SearchParam'
- $ref: '#/components/parameters/FiltersParam'
responses:
'200':
description: Goals retrieved successfully
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/DepartmentGoal'
'400': { $ref: '#/components/responses/BadRequest' }
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
'404': { $ref: '#/components/responses/NotFound' }
post:
tags:
- Goals
summary: Create department goal
description: |
Create a new department goal.
Access control:
- The provided `departments` must be a subset of the user's departments unless the user has `superuser`.
operationId: createDepartmentGoal
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentGoalCreate'
responses:
'201':
description: Department goal created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentGoal'
'400': { $ref: '#/components/responses/BadRequest' }
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
put:
tags:
- Goals
summary: Update department goal
description: |
Update an existing department goal by `id`.
Access control:
- The creator (`created_by`) may update regardless of department membership.
- Otherwise the user must satisfy the same subset rule as for read access, and any new `departments` provided must also be a subset unless the user has `superuser`.
operationId: updateDepartmentGoal
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentGoalUpdate'
responses:
'200':
description: Department goal updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentGoal'
'400': { $ref: '#/components/responses/BadRequest' }
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
'404': { $ref: '#/components/responses/NotFound' }
/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.
Permission required: `goals_department_progress_alert_test`.
Access control:
- The caller must be a superuser or belong to all departments targeted by the goal.
Behavior:
- Looks up the goal by `id`.
- Rebuilds the criteria from stored JSON and attaches the goal's departments.
- Renders the alert using the server-side renderer (respecting progress type/style/format and destination limits).
- Sends the alert to Slack, Email, or SMS depending on `progress_alert_destination`, unless overridden.
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`.
Access control:
- The creator (`created_by`) may delete regardless of department membership.
- Otherwise the user must satisfy the subset rule or have `superuser`.
operationId: deleteDepartmentGoal
parameters:
- name: id
in: query
required: true
schema: { type: integer }
description: ID of the goal to delete
responses:
'200':
description: Department goal deleted successfully
'400': { $ref: '#/components/responses/BadRequest' }
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
'404': { $ref: '#/components/responses/NotFound' }
'/order-bookings':
post:
tags:
- Bookings
summary: Create order booking
operationId: createOrderBooking
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [department, reg_1, datetime, items]
properties:
customer_number: {type: integer}
department: {type: integer}
reg_1: {type: string}
reg_2: {type: string}
reg_3: {type: string}
datetime: {type: string, format: date-time}
note: {type: string}
reference: {type: string}
po: {type: string}
pickup: {type: boolean}
items:
type: array
items:
type: object
required: [id, quantity]
properties:
id: {type: integer}
quantity: {type: integer}
responses:
'200':
description: Success
put:
tags:
- Bookings
summary: Update order booking
operationId: updateOrderBooking
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [id]
properties:
id: {type: integer}
customer_number: {type: integer}
department: {type: integer}
reg_1: {type: string}
reg_2: {type: string}
reg_3: {type: string}
datetime: {type: string, format: date-time}
note: {type: string}
reference: {type: string}
po: {type: string}
pickup: {type: boolean}
order_id: {type: integer}
items:
type: array
items:
type: object
required: [id, quantity]
properties:
id: {type: integer}
quantity: {type: integer}
responses:
'200':
description: Success
delete:
tags:
- Bookings
summary: Delete order booking
operationId: deleteOrderBooking
parameters:
- name: id
in: query
required: true
schema: {type: integer}
responses:
'200':
description: Success
/order-bookings/complete:
post:
tags:
- Bookings
summary: Complete order booking
operationId: completeOrderBooking
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [id]
properties:
id: {type: integer}
safety_seal: {type: integer}
responses:
'200':
description: Success
/admin/bookings/sync:
post:
tags:
- Bookings
summary: Sync booking from external system
operationId: syncBooking
responses:
'200':
description: Success
/admin/bookings/department/count:
get:
tags:
- Bookings
summary: Get department unfulfilled bookings count
operationId: getDepartmentBookingCount
parameters:
- name: department_id
in: query
required: true
schema: {type: integer}
responses:
'200':
description: Success
/user/bookings/washcertificate/download:
post:
tags:
- Bookings
summary: Get download link for own wash certificate
operationId: downloadOwnWashCertificate
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [id]
properties:
id: {type: integer}
responses:
'200':
description: Success
/bookings/download_pdf:
get:
tags:
- Bookings
summary: Download booking PDF
operationId: downloadBookingPdf
parameters:
- name: id
in: query
required: true
schema: {type: integer}
responses:
'200':
description: Success
/admin/bookings/delete:
post:
tags:
- Bookings
summary: Delete booking (admin)
operationId: adminDeleteBooking
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [id]
properties:
id: {type: integer}
responses:
'200':
description: Success
/superuser/bookings/sync/all:
post:
tags:
- Bookings
summary: Sync all bookings from external system
operationId: syncAllBookings
responses:
'200':
description: Success
/admin/bookings/completeWashWithoutWashCertificate:
post:
tags:
- Bookings
summary: Complete wash without wash certificate
operationId: completeWashWithoutWashCertificate
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [id]
properties:
id: {type: integer}
responses:
'200':
description: Success
/user/bookings/delete:
post:
tags:
- Bookings
summary: Delete own booking
operationId: deleteOwnBooking
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [id]
properties:
id: {type: integer}
responses:
'200':
description: Success
# Invoices Endpoints
/invoices/draft:
get:
tags:
- Invoices
summary: List draft invoices
description: Retrieve a list of draft invoices
operationId: listDraftInvoices
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
responses:
'200':
description: Draft invoices retrieved successfully
/invoices/draft/close:
post:
tags:
- Invoices
summary: Close draft invoice
description: Close a draft invoice
operationId: closeDraftInvoice
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
id:
type: integer
responses:
'200':
description: Draft invoice closed successfully
/invoices/pdf:
get:
tags:
- Invoices
summary: Get invoice PDF
description: Download an invoice as PDF
operationId: getInvoicePdf
parameters:
- name: id
in: query
required: true
schema:
type: integer
responses:
'200':
description: PDF retrieved successfully
content:
application/pdf:
schema:
type: string
format: binary
/user/invoices:
get:
tags:
- Invoices
summary: Get user invoices
description: Retrieve invoices for the authenticated user
operationId: getUserInvoices
responses:
'200':
description: User invoices retrieved successfully
/collected-invoices:
get:
tags:
- Invoices
summary: List collected invoices
description: Get list of collected invoices
operationId: listCollectedInvoices
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
responses:
'200':
description: Collected invoices retrieved successfully
post:
tags:
- Invoices
summary: Create collected invoice
description: Create a new collected invoice
operationId: createCollectedInvoice
responses:
'201':
description: Collected invoice created successfully
put:
tags:
- Invoices
summary: Update collected invoice
description: Update a collected invoice
operationId: updateCollectedInvoice
responses:
'200':
description: Collected invoice updated successfully
/collected-invoices/ready-to-invoice:
get:
tags:
- Invoices
summary: Get invoices ready to process
description: Get collected invoices that are ready to be processed
operationId: getReadyToInvoice
responses:
'200':
description: Ready invoices retrieved successfully
/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.
Returns totals from both sources, their difference, and any warnings detected during comparison.
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'
/superuser/invoicing/period:
get:
tags:
- Invoices
summary: Get invoicing periods
description: Retrieve invoicing periods for superusers
operationId: getInvoicingPeriods
parameters:
- name: dateFrom
in: query
required: true
schema: {type: string, format: date}
- name: dateTo
in: query
required: true
schema: {type: string, format: date}
responses:
'200':
description: Invoicing periods retrieved successfully
/superuser/invoicing/period/distribution/fixed-pricing:
get:
tags:
- Invoices
summary: Get fixed pricing distribution
description: Get invoicing distribution for fixed pricing items
operationId: getInvoicingFixedPricingDistribution
parameters:
- name: dateFrom
in: query
required: true
schema: {type: string, format: date}
- name: dateTo
in: query
required: true
schema: {type: string, format: date}
responses:
'200':
description: Fixed pricing distribution retrieved successfully
/superuser/invoicing/period/distribution/wash-subscriptions:
get:
tags:
- Invoices
summary: Get wash subscriptions distribution
description: Get invoicing distribution for wash subscriptions
operationId: getInvoicingWashSubscriptionsDistribution
parameters:
- name: dateFrom
in: query
required: true
schema: {type: string, format: date}
- name: dateTo
in: query
required: true
schema: {type: string, format: date}
responses:
'200':
description: Wash subscriptions distribution retrieved successfully
# Vehicles Endpoints
/vehicles:
get:
tags:
- Vehicles
summary: List vehicles
description: |
List vehicles or fetch a specific vehicle when `id` is provided.
- When `id` is present, returns a single vehicle object (404 if not found).
- Otherwise returns a paginated list of vehicles.
Permissions:
- Own scope: `list_own_vehicles` (linked to subuser node `VEHICLES_LIST`).
- Broader scope: `list_vehicles_other`.
Subusers may specify header `X-Customer-Number` to target a specific customer. If the broader
permission is missing, the list will automatically be restricted to the effective customer context.
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.
Permissions:
- Own scope: `add_vehicle` (linked to subuser node `VEHICLES_ADD`).
- Broader scope: `add_vehicle_other`.
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}
'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.
Permissions:
- Own scope: `edit_vehicle` (linked to subuser node `VEHICLES_EDIT`).
- Broader scope: `edit_vehicle_other`.
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}
'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.
Permissions:
- Own scope: `delete_vehicle` (linked to subuser node `VEHICLES_DELETE`).
- Broader scope: `delete_vehicle_other`.
parameters:
- $ref: '#/components/parameters/XCustomerNumber'
- name: id
in: query
required: true
schema: {type: integer}
responses:
'200': {description: Vehicle deleted}
'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.
Permissions:
- Own scope: `list_vehicle_addon_own` (linked to subuser node `VEHICLES_LIST`).
- Broader scope: `list_vehicles_addon_other`.
operationId: getAvailableVehicleAddons
parameters:
- $ref: '#/components/parameters/XCustomerNumber'
- name: id
in: query
required: true
schema:
type: integer
responses:
'200':
description: Available addons retrieved successfully
'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.
Permissions:
- Own scope: `toggle_vehicle_addon_own` (linked to subuser node `VEHICLES_EDIT`).
- Broader scope: `toggle_vehicle_addon_other`.
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
'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}
/department/vehicle/customer-suggestions:
get:
tags:
- Vehicles
summary: Get vehicle customer suggestions
operationId: getVehicleCustomerSuggestions
parameters:
- name: reg
in: query
required: true
schema: {type: string}
responses:
'200': {description: Success}
/vehicles/set-auto-start-on-lpr:
post:
tags:
- Vehicles
summary: Set auto start on LPR
operationId: setVehicleAutoStartOnLpr
description: |
Enable or disable automatic start on LPR for a vehicle in XL Vask.
Permissions:
- Own scope: `set_auto_start_on_lpr` (linked to subuser node `VEHICLES_EDIT`).
- Broader scope: `set_auto_start_on_lpr_other`.
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}
'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.
Permissions:
- Own scope: `set_vehicle_type_id` (linked to subuser node `VEHICLES_EDIT`).
- Broader scope: `set_vehicle_type_id_other`.
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}
'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}
/vehicles/status:
get:
tags:
- Vehicles
summary: Get vehicle status
operationId: getVehicleStatus
parameters:
- name: reg
in: query
required: true
schema: {type: string}
responses:
'200': {description: Success}
/vehicles/search:
get:
tags:
- Vehicles
summary: Search vehicles
operationId: searchVehicles
parameters:
- name: search
in: query
required: true
schema: {type: string}
responses:
'200': {description: Success}
# Notifications Endpoints
/notifications:
get:
tags:
- Notifications
summary: List notifications
description: Get list of notifications
operationId: listNotifications
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
responses:
'200':
description: Notifications retrieved successfully
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Notification'
post:
tags:
- Notifications
summary: Create notification
description: Create a new notification
operationId: createNotification
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NotificationCreate'
responses:
'201':
description: Notification created successfully
delete:
tags:
- Notifications
summary: Delete notification
description: Delete a notification
operationId: deleteNotification
parameters:
- name: id
in: query
required: true
schema:
type: integer
responses:
'200':
description: Notification deleted successfully
# Statistics Endpoints
/statistics/bookings/new:
get:
tags:
- Statistics
summary: Get new bookings statistics
description: Get statistics for new bookings
operationId: getNewBookingsStats
responses:
'200':
description: New bookings statistics retrieved successfully
/orders/module/stripe/payment_intent:
get:
tags:
- Orders
summary: Get Stripe payment intent
operationId: getStripePaymentIntent
parameters:
- name: id
in: query
required: true
schema: {type: integer}
responses:
'200':
description: Success
post:
tags:
- Orders
summary: Create Stripe payment intent
operationId: createStripePaymentIntent
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [id, reader]
properties:
id: {type: integer}
reader: {type: string}
tax_percentage: {type: integer}
responses:
'200':
description: Success
delete:
tags:
- Orders
summary: Delete Stripe payment intent
operationId: deleteStripePaymentIntent
parameters:
- name: id
in: query
required: true
schema: {type: integer}
responses:
'200':
description: Success
/orders/module/stripe/payment_intent/capture:
post:
tags:
- Orders
summary: Capture Stripe payment intent
operationId: captureStripePaymentIntent
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [id]
properties:
id: {type: integer}
responses:
'200':
description: Success
/orders/module/stripe/debug/simulate_payment:
post:
tags:
- Orders
summary: Simulate Stripe payment
operationId: simulateStripePayment
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [id]
properties:
id: {type: integer}
responses:
'200':
description: Success
/statistics/orders/new:
get:
tags:
- Statistics
summary: Get new orders statistics
description: Get statistics for new orders
operationId: getNewOrdersStats
responses:
'200':
description: New orders statistics retrieved successfully
/statistics/income/today:
get:
tags:
- Statistics
summary: Get today's income
description: Get income statistics for today
operationId: getTodayIncome
responses:
'200':
description: Today's income statistics retrieved successfully
/statistics/income/yesterday:
get:
tags:
- Statistics
summary: Get yesterday's income
description: Get income statistics for yesterday
operationId: getYesterdayIncome
responses:
'200':
description: Yesterday's income statistics retrieved successfully
/statistics/income/this-month:
get:
tags:
- Statistics
summary: Get this month's income
description: Get income statistics for the current month
operationId: getThisMonthIncome
responses:
'200':
description: This month's income statistics retrieved successfully
/statistics/income/last-month:
get:
tags:
- Statistics
summary: Get last month's income
description: Get income statistics for the previous month
operationId: getLastMonthIncome
responses:
'200':
description: Last month's income statistics retrieved successfully
/statistics/income/this-year:
get:
tags:
- Statistics
summary: Get this year's income
description: Get income statistics for the current year
operationId: getThisYearIncome
responses:
'200':
description: This year's income statistics retrieved successfully
/statistics/income/departments:
get:
tags:
- Statistics
summary: Get total income today by departments
operationId: getTotalIncomeTodayByDepartments
responses:
'200': {description: Success}
/statistics/economic/totals:
get:
tags:
- Statistics
summary: Get total economic statistics
operationId: getEconomicTotals
responses:
'200': {description: Success}
/statistics/economic/totals/department_sent_invoice_totals:
get:
tags:
- Statistics
summary: Get department sent invoice totals
operationId: getDepartmentSentInvoiceTotals
responses:
'200': {description: Success}
/statistics/economic/totals/department_draft_invoice_totals:
get:
tags:
- Statistics
summary: Get department draft invoice totals
operationId: getDepartmentDraftInvoiceTotals
responses:
'200': {description: Success}
# Worker Endpoints
/worker/version:
get:
tags:
- Worker
summary: Get worker version
description: Get the current version of the system worker
operationId: getWorkerVersion
responses:
'200':
description: Worker version retrieved successfully
/worker/update-version:
get:
tags:
- Worker
summary: Update worker version
description: Set the target version for the worker update
operationId: updateWorkerVersion
parameters:
- name: version
in: query
required: true
schema:
type: string
responses:
'200':
description: Version update target set successfully
/worker/status:
get:
tags:
- Worker
summary: Get worker status
description: Get detailed status of the system worker
operationId: getWorkerStatus
responses:
'200':
description: Worker status retrieved successfully
/worker/debug:
get:
tags:
- Worker
summary: Debug worker
description: Execute debug commands on the worker (often restricted)
operationId: debugWorker
responses:
'200':
description: Debug information retrieved successfully
'403':
$ref: '#/components/responses/Forbidden'
/worker/debug/on:
get:
tags:
- Worker
summary: Enable worker debug
operationId: enableWorkerDebug
responses:
'200':
description: Worker debug enabled
'403':
$ref: '#/components/responses/Forbidden'
/worker/debug/off:
get:
tags:
- Worker
summary: Disable worker debug
operationId: disableWorkerDebug
responses:
'200':
description: Worker debug disabled
'403':
$ref: '#/components/responses/Forbidden'
/worker/licenseplates:
get:
tags:
- Worker
summary: Get unique license plates
description: Fetch all unique license plates from various database tables
operationId: getWorkerLicensePlates
responses:
'200':
description: License plates retrieved successfully
'403':
$ref: '#/components/responses/Forbidden'
/economic/doesCustomerExist:
get:
tags:
- Modules
summary: Check if customer exists in e-conomic
operationId: checkEconomicCustomerExists
parameters:
- name: cvr
in: query
required: true
schema:
type: string
responses:
'200':
description: Customer check completed
'404':
$ref: '#/components/responses/NotFound'
/cvr/lookup:
get:
tags:
- Modules
summary: Lookup CVR information
description: Get detailed information for a CVR number
operationId: lookupCvr
parameters:
- name: cvr
in: query
required: true
schema:
type: string
responses:
'200':
description: CVR information retrieved successfully
/cvr/search:
get:
tags:
- Modules
summary: Search CVR
description: Search for companies by name or CVR
operationId: searchCvr
parameters:
- name: query
in: query
required: true
schema:
type: string
minLength: 2
responses:
'200':
description: Search results retrieved successfully
# Plate Scans Endpoints
/numberplatescans:
get:
tags:
- Plate Scans
summary: List plate scans
description: Get a list of license plate scans
operationId: listPlateScans
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
- $ref: '#/components/parameters/SearchParam'
responses:
'200':
description: Plate scans retrieved successfully
post:
tags:
- Plate Scans
summary: Record plate scan
description: Record a new license plate scan
operationId: recordPlateScan
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- plate
- lane_id
properties:
plate:
type: string
lane_id:
type: integer
responses:
'201':
description: Plate scan recorded successfully
/numberplatescans/department:
post:
tags:
- Plate Scans
summary: Record plate scan for department
operationId: recordDepartmentPlateScan
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- plate
- department_id
properties:
plate:
type: string
department_id:
type: integer
responses:
'201':
description: Plate scan recorded successfully
/numberplatescans/post:
get:
tags:
- Plate Scans
summary: Get post-scan results
operationId: getPlateScanPostResults
responses:
'200':
description: Post-scan results retrieved successfully
/numberplatescanners:
get:
tags:
- Plate Scans
summary: List plate scanners
description: Get a list of all number plate scanners
operationId: listPlateScanners
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
- $ref: '#/components/parameters/SearchParam'
responses:
'200':
description: Plate scanners retrieved successfully
post:
tags:
- Plate Scans
summary: Add plate scanner
operationId: addPlateScanner
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [department_id, name, notes]
properties:
department_id: {type: integer}
name: {type: string}
notes: {type: string}
responses:
'201':
description: Plate scanner added successfully
put:
tags:
- Plate Scans
summary: Update plate scanner
operationId: updatePlateScanner
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [id, department_id, name, notes]
properties:
id: {type: integer}
department_id: {type: integer}
name: {type: string}
notes: {type: string}
responses:
'200':
description: Plate scanner updated successfully
/department/numberplatescanners:
get:
tags:
- Plate Scans
summary: List department plate scanners
operationId: listDepartmentPlateScanners
parameters:
- name: id
in: query
required: true
schema:
type: integer
responses:
'200':
description: Department plate scanners retrieved successfully
/relay/button/press/post:
get:
tags:
- Plate Scans
summary: Add button press
operationId: addButtonPress
parameters:
- name: token
in: query
required: true
schema: {type: string}
responses:
'201':
description: Button press recorded
# Module - e-conomic Endpoints
/economic/customers/import:
post:
tags:
- Modules
summary: Import e-conomic customers
description: Import customers from e-conomic
operationId: importEconomicCustomers
responses:
'200':
description: Customers imported successfully
/economic/departments:
get:
tags:
- Modules
summary: Get e-conomic departments
operationId: getEconomicDepartments
responses:
'200':
description: Success
/economic/products:
get:
tags:
- Modules
summary: Get e-conomic products
operationId: getEconomicProducts
responses:
'200':
description: Success
/modules/economic/customer:
get:
tags:
- Modules
summary: Get e-conomic customer details
operationId: getEconomicCustomer
parameters:
- name: customer_number
in: query
required: true
schema: {type: integer}
responses:
'200':
description: Success
post:
tags:
- Modules
summary: Create e-conomic customer
operationId: createEconomicCustomer
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [customer_number, cvr, email, phone, name]
properties:
customer_number: {type: integer}
cvr: {type: integer}
email: {type: string}
phone: {type: integer}
name: {type: string}
responses:
'200':
description: Success
/economic/layouts:
get:
tags:
- Modules
summary: Get e-conomic layouts
description: Get available invoice layouts from e-conomic
operationId: getEconomicLayouts
responses:
'200':
description: Layouts retrieved successfully
/economic/payment-terms:
get:
tags:
- Modules
summary: Get e-conomic payment terms
description: Get available payment terms from e-conomic
operationId: getEconomicPaymentTerms
responses:
'200':
description: Payment terms retrieved successfully
/economic/invoice/draft/export:
post:
tags:
- Modules
summary: Export draft invoice to e-conomic
description: Export a draft invoice to e-conomic
operationId: exportDraftInvoiceToEconomic
responses:
'200':
description: Draft invoice exported successfully
/economic/invoice/export:
post:
tags:
- Modules
summary: Export invoice to e-conomic
description: Export a booked invoice to e-conomic
operationId: exportInvoiceToEconomic
responses:
'200':
description: Invoice exported successfully
# Module - Stripe Endpoints
/modules/stripe/customers:
get:
tags:
- Modules
summary: List Stripe customers
description: Get list of Stripe customers
operationId: listStripeCustomers
responses:
'200':
description: Stripe customers retrieved successfully
/modules/stripe/products:
get:
tags:
- Modules
summary: List Stripe products
description: Get list of Stripe products
operationId: listStripeProducts
responses:
'200':
description: Stripe products retrieved successfully
/modules/stripe/prices:
get:
tags:
- Modules
summary: List Stripe prices
description: Get list of Stripe prices
operationId: listStripePrices
responses:
'200':
description: Stripe prices retrieved successfully
/modules/stripe/invoice:
post:
tags:
- Modules
summary: Create Stripe invoice
description: Create an invoice in Stripe
operationId: createStripeInvoice
responses:
'201':
description: Stripe invoice created successfully
/modules/stripe/terminal/readers:
get:
tags:
- Modules
summary: List Stripe terminal readers
operationId: listStripeTerminalReaders
responses:
'200':
description: Success
/modules/stripe/terminal/locations:
get:
tags:
- Modules
summary: List Stripe terminal locations
operationId: listStripeTerminalLocations
responses:
'200':
description: Success
/modules/stripe/department/terminal/location:
get:
tags:
- Modules
summary: Get department terminal location
operationId: getDepartmentTerminalLocation
parameters:
- name: id
in: query
required: true
schema: {type: integer}
responses:
'200':
description: Success
post:
tags:
- Modules
summary: Set department terminal location
operationId: setDepartmentTerminalLocation
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [id, location]
properties:
id: {type: integer}
location: {type: string}
responses:
'200':
description: Success
/modules/stripe/department/terminal/readers:
get:
tags:
- Modules
summary: Get department terminal readers
operationId: getDepartmentTerminalReaders
parameters:
- name: id
in: query
required: true
schema: {type: integer}
responses:
'200':
description: Success
# Module - Backup Endpoints
/modules/backup/backups:
get:
tags:
- Modules
summary: List backup modules
operationId: listBackupModules
responses:
'200':
description: Success
post:
tags:
- Modules
summary: Create backup module
operationId: createBackupModule
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [name, description]
properties:
name: {type: string}
description: {type: string}
responses:
'200':
description: Success
# Module - XLVask Endpoints
/modules/xlvask/usageLog:
get:
tags:
- Modules
summary: Get XLVask usage logs
description: Retrieve usage logs from XLVask system
operationId: getXlvaskUsageLogs
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
responses:
'200':
description: Usage logs retrieved successfully
/modules/xlvask/vehicles:
get:
tags:
- Modules
summary: List XLVask vehicles
description: Get list of vehicles from XLVask
operationId: listXlvaskVehicles
responses:
'200':
description: XLVask vehicles retrieved successfully
/modules/xlvask/customers:
get:
tags:
- Modules
summary: List XLVask customers
description: Get list of customers from XLVask
operationId: listXlvaskCustomers
responses:
'200':
description: XLVask customers retrieved successfully
/modules/action-logs:
get:
tags:
- Modules
summary: List module action logs
description: Retrieve a paginated list of module action logs with searching and filtering
operationId: listModuleActionLogs
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/LimitParam'
- $ref: '#/components/parameters/SearchParam'
- $ref: '#/components/parameters/FiltersParam'
responses:
'200':
description: Module action logs retrieved successfully
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ModuleActionLog'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
# Module - Self-Serve Endpoints
/modules/self-serve/lane/status:
get:
tags:
- Modules
summary: Get self-serve lane status
description: Retrieve the current status of a self-serve lane
operationId: getSelfServeLaneStatus
parameters:
- name: lane_id
in: query
required: true
schema:
type: integer
responses:
'200':
description: Lane status retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/SelfServeLaneStatus'
/modules/self-serve/lane/command:
post:
tags:
- Modules
summary: Send self-serve lane command
description: Send a command (e.g., start, stop, reset) to a self-serve lane
operationId: sendSelfServeLaneCommand
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- lane_id
- command
properties:
lane_id:
type: integer
command:
type: string
enum: [START, STOP, RESET, RESERVE, RELEASE]
license_plate:
type: string
description: Required for START command
responses:
'200':
description: Command sent successfully
content:
application/json:
schema:
$ref: '#/components/schemas/SelfServeLaneStatus'
/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,
derived from the tasks currently shown to the user after answering the self-serve questions.
This endpoint does not activate anything by itself; it only sets what is allowed to be activated.
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
include `MACHINE` (set via `/modules/self-serve/lane/services/allowed`). The relay is never automatically
enabled; an explicit call to this endpoint is required.
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.
Also ensures the lane is marked as OCCUPIED and IN_WASH with a wash start timestamp if not already set.
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
(simulating a started wash without machine assistance).
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'
# Module - Other Integration Endpoints
/modules/motorapi/lookup:
get:
tags:
- Modules
summary: Lookup vehicle via MotorAPI
description: Look up vehicle information using license plate
operationId: motorApiLookup
parameters:
- name: plate
in: query
required: true
schema:
type: string
responses:
'200':
description: Vehicle information retrieved successfully
/modules/virkdata/search:
get:
tags:
- Modules
summary: Search VirkData
description: Search for company information in VirkData
operationId: virkdataSearch
parameters:
- name: search
in: query
required: true
schema:
type: string
responses:
'200':
description: Company information retrieved successfully
/modules/fxratesapi/rate:
get:
tags:
- Modules
summary: Get exchange rate
description: Get current exchange rate
operationId: getExchangeRate
parameters:
- name: from
in: query
required: true
schema:
type: string
- name: to
in: query
required: true
schema:
type: string
responses:
'200':
description: Exchange rate retrieved successfully
/modules/fxratesapi/rates:
get:
tags:
- Modules
summary: Get all exchange rates
description: Get all available exchange rates
operationId: getAllExchangeRates
responses:
'200':
description: Exchange rates retrieved successfully
/modules/entra/users:
get:
tags:
- Modules
summary: List Microsoft Entra users
description: Get list of users from Microsoft Entra (Azure AD)
operationId: listEntraUsers
responses:
'200':
description: Entra users retrieved successfully
# Attachments Endpoints
/attachments/upload:
post:
tags:
- Attachments
summary: Upload attachment
description: Upload a file attachment
operationId: uploadAttachment
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
format: binary
responses:
'201':
description: Attachment uploaded successfully
'400':
$ref: '#/components/responses/BadRequest'
/orders/attachments:
get:
tags:
- Attachments
summary: List order attachments
description: Get attachments for an order
operationId: listOrderAttachments
parameters:
- name: order_id
in: query
required: true
schema:
type: integer
responses:
'200':
description: Order attachments retrieved successfully
/orders/attachments/upload:
post:
tags:
- Attachments
summary: Upload order attachment
description: Upload an attachment to an order
operationId: uploadOrderAttachment
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
order_id:
type: integer
file:
type: string
format: binary
responses:
'201':
description: Order attachment uploaded successfully
/orders/attachments/download:
get:
tags:
- Attachments
summary: Download order attachment
description: Download a specific order attachment
operationId: downloadOrderAttachment
parameters:
- name: id
in: query
required: true
schema:
type: integer
responses:
'200':
description: Attachment downloaded successfully
content:
application/octet-stream:
schema:
type: string
format: binary
# Forms Endpoints
/form:
get:
tags:
- Forms
summary: Get form
description: Retrieve a form definition
operationId: getForm
parameters:
- name: id
in: query
schema:
type: integer
responses:
'200':
description: Form retrieved successfully
post:
tags:
- Forms
summary: Submit form
description: Submit a form
operationId: submitForm
requestBody:
required: true
content:
application/json:
schema:
type: object
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
# Permissions Endpoints
/permissions:
get:
tags:
- Users
summary: List permissions
description: Get list of all available permissions
operationId: listPermissions
responses:
'200':
description: Permissions retrieved successfully
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Permission'
# Customer Management Endpoints
/customer/attributes:
get:
tags:
- Users
summary: Get customer attributes
description: Get custom attributes for a customer
operationId: getCustomerAttributes
parameters:
- name: customer_id
in: query
schema:
type: integer
responses:
'200':
description: Customer attributes retrieved successfully
post:
tags:
- Users
summary: Add customer attribute
description: Add a custom attribute to a customer
operationId: addCustomerAttribute
responses:
'201':
description: Customer attribute added successfully
delete:
tags:
- Users
summary: Delete customer attribute
description: Remove a custom attribute from a customer
operationId: deleteCustomerAttribute
responses:
'200':
description: Customer attribute deleted successfully
/customer/notes:
get:
tags:
- Users
summary: Get customer notes
description: Get notes for a customer
operationId: getCustomerNotes
parameters:
- name: customer_id
in: query
schema:
type: integer
responses:
'200':
description: Customer notes retrieved successfully
post:
tags:
- Users
summary: Add customer note
description: Add a note to a customer
operationId: addCustomerNote
responses:
'201':
description: Customer note added successfully
delete:
tags:
- Users
summary: Delete customer note
description: Remove a note from a customer
operationId: deleteCustomerNote
responses:
'200':
description: Customer note deleted successfully
/customers/search:
post:
tags:
- Users
summary: Search customers
description: Search for customers using various criteria
operationId: searchCustomers
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
query:
type: string
responses:
'200':
description: Customers found successfully
# Configuration Endpoints
/economic/config:
get:
tags: [Config]
summary: Get e-conomic config
operationId: getEconomicConfig
responses:
'200':
description: e-conomic configuration retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/EconomicConfigListResponse'
post:
tags: [Config]
summary: Update e-conomic config
operationId: updateEconomicConfig
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: getRecaptchaConfig
responses:
'200':
description: reCAPTCHA configuration retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/RecaptchaConfigListResponse'
post:
tags: [Config]
summary: Update reCAPTCHA config
operationId: updateRecaptchaConfig
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
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
responses:
'200':
description: Email configuration test completed
content:
application/json:
schema:
$ref: '#/components/schemas/ModuleConfigTestResponse'
/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
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
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
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
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
responses:
'200':
description: FXRatesAPI 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
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
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
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
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
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
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
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
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
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
'400':
$ref: '#/components/responses/BadRequest'
'403':
$ref: '#/components/responses/Forbidden'
post:
tags:
- Branding
summary: Add branding option
description: Create a new branding option
operationId: addBrandingOption
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [name, description, cvr]
properties:
name: {type: string}
description: {type: string}
cvr: {type: integer}
responses:
'200':
description: Branding option added successfully
'403':
$ref: '#/components/responses/Forbidden'
put:
tags:
- Branding
summary: Edit branding option
description: Update an existing branding option
operationId: editBrandingOption
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [id]
properties:
id: {type: integer}
name: {type: string}
description: {type: string}
cvr: {type: integer}
responses:
'200':
description: Branding option updated successfully
'403':
$ref: '#/components/responses/Forbidden'
/roles:
get:
tags:
- Roles
summary: List roles
operationId: listRoles
responses:
'200':
description: Success
post:
tags:
- Roles
summary: Add role
operationId: addRole
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [name]
properties:
name: {type: string}
responses:
'200':
description: Success
put:
tags:
- Roles
summary: Edit role
operationId: editRole
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [id, name]
properties:
id: {type: integer}
name: {type: string}
responses:
'200':
description: Success
/roles/permissions:
post:
tags:
- Roles
summary: Add permission to role
operationId: addRolePermission
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [role_id, permission]
properties:
role_id: {type: integer}
permission: {type: string}
responses:
'200':
description: Success
delete:
tags:
- Roles
summary: Remove permission from role
operationId: removeRolePermission
parameters:
- name: role_id
in: query
required: true
schema: {type: integer}
- name: permission
in: query
required: true
schema: {type: string}
responses:
'200':
description: Success
/roles/clone:
post:
tags:
- Roles
summary: Clone role
operationId: cloneRole
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [role_id, name]
properties:
role_id: {type: integer}
name: {type: string}
responses:
'200':
description: Success
/modules/washcertificates:
get:
tags:
- Modules
summary: List wash certificates
operationId: listWashCertificates
responses:
'200':
description: Success
/modules/xlvask/services/usage/orders:
get:
tags:
- Modules
summary: Get XLVask usage orders
operationId: getXlvaskUsageOrders
responses:
'200':
description: Success
/modules/xlvask/services/usage/orders/fast-link:
get:
tags:
- Modules
summary: Get XLVask usage orders fast link
operationId: getXlvaskUsageOrdersFastLink
responses:
'200':
description: Success
/superuser/department:
get:
tags:
- Departments
summary: List departments (superuser)
operationId: listSuperuserDepartments
responses:
'200':
description: Success
/superuser/department/prices:
get:
tags:
- Departments
summary: Get department prices
operationId: getDepartmentPrices
parameters:
- name: department_id
in: query
required: true
schema: {type: integer}
responses:
'200':
description: Success
post:
tags:
- Departments
summary: Set department price
operationId: setDepartmentPrice
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [department_id, product_id, price]
properties:
department_id: {type: integer}
product_id: {type: integer}
price: {type: integer}
responses:
'200':
description: Success
/superuser/department/variables:
get:
tags:
- Departments
summary: Get department variables
operationId: getDepartmentVariables
parameters:
- name: department_id
in: query
required: true
schema: {type: integer}
responses:
'200':
description: Success
post:
tags:
- Departments
summary: Set department variable
operationId: setDepartmentVariable
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [department_id, variable, value]
properties:
department_id: {type: integer}
variable: {type: string}
value: {type: string}
responses:
'200':
description: Success
/departments/daily-reports:
get:
tags:
- Departments
summary: List daily reports
operationId: listDailyReports
responses:
'200':
description: Success
post:
tags:
- Departments
summary: Add daily report
operationId: addDailyReport
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [department_id, date, report]
properties:
department_id: {type: integer}
date: {type: string}
report: {type: string}
responses:
'200':
description: Success
put:
tags:
- Departments
summary: Edit daily report
operationId: editDailyReport
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [id, report]
properties:
id: {type: integer}
report: {type: string}
responses:
'200':
description: Success
/departments/daily-reports/get:
get:
tags:
- Departments
summary: Get daily report
operationId: getDailyReport
parameters:
- name: department_id
in: query
required: true
schema: {type: integer}
- name: date
in: query
required: true
schema: {type: string}
responses:
'200':
description: Success
/departments/daily-reports/product-count:
get:
tags:
- Departments
summary: Get product count for daily reports
operationId: getDailyReportProductCount
parameters:
- name: date
in: query
required: true
schema: {type: string}
- name: date_to
in: query
required: false
schema: {type: string}
- name: department_id
in: query
required: true
schema: {type: integer}
- name: product_id
in: query
required: true
schema: {type: integer}
responses:
'200':
description: Success
/departments/daily-reports/transaction-count:
get:
tags:
- Departments
summary: Get transaction count for daily reports
operationId: getDailyReportTransactionCount
parameters:
- name: date
in: query
required: true
schema: {type: string}
- name: date_to
in: query
required: false
schema: {type: string}
- name: department_id
in: query
required: true
schema: {type: integer}
responses:
'200':
description: Success
/departments/daily-reports/bookings-count:
get:
tags:
- Departments
summary: Get bookings count for daily reports
operationId: getDailyReportBookingsCount
parameters:
- name: date
in: query
required: true
schema: {type: string}
- name: department_id
in: query
required: true
schema: {type: integer}
responses:
'200':
description: Success
# Account Security - Passkeys
/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
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
/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
'404':
description: Not found
delete:
tags:
- Security
summary: Delete a passkey
operationId: deletePasskey
parameters:
- in: path
name: id
required: true
schema:
type: integer
responses:
'200':
description: Passkey deleted
'404':
description: Not found
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.
Required on customer-scoped endpoints when authenticated as a subuser unless
the target customer can be inferred from context.
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
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]
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]
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]
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]
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]
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: draft_total minus booked_total when both are available
example: 0
internal_total:
type: number
format: float
description: Internal total amount for the collected invoice
example: 694
order_ids:
type: array
description: List of order IDs included in the collected invoice
items:
type: integer
example: [38679, 39210]
required:
- collected_invoice_id
- internal_total
- order_ids
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
DepartmentSelfserveQuestion:
type: object
properties:
id:
type: integer
description: Question ID
department:
type: integer
description: Department ID
lane:
type: integer
description: Lane ID
product:
type: integer
description: Product ID
condition_id:
type: integer
description: Question condition object ID
nullable: true
question:
type: string
description: Question text
description:
type: string
description: Question description
order_priority:
type: integer
description: Display order priority (lower numbers shown first)
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
DepartmentSelfserveTask:
type: object
properties:
id:
type: integer
description: Task ID
department:
type: integer
description: Department ID
lane:
type: integer
description: Lane ID
product:
type: integer
description: Product ID
condition_id:
type: integer
description: Condition ID (if conditional task)
nullable: true
task:
type: string
description: Task text
description:
type: string
description: Task description
order_priority:
type: integer
description: Display order priority (lower numbers shown first)
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
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
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
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
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.
If type is 'PHONE_CALL', 'phone_number' and 'call_duration_threshold' are required.
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.
If the relay 'type' is 'TRIGGER', 'what_happens' and 'webhook_token' are required.
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.
Accepted values: DAILY, WEEKLY, MONTHLY, CHANGED, NONE.
The canonical field name is snake_case `progress_alert_frequency`.
For backward-compatibility the API also accepts camelCase `progressAlertFrequency` on input.
enum: [DAILY, WEEKLY, MONTHLY, CHANGED, NONE]
example: DAILY
progress_alert_destination:
type: string
description: |
Destination/channel where progress alerts should be delivered.
Accepted values: SLACK, EMAIL, SMS, NONE.
The canonical field name is snake_case `progress_alert_destination`.
For backward-compatibility the API also accepts camelCase `progressAlertDestination` on input.
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.
Accepted values: ALL, PERCENTAGE_ONLY, COUNT_ONLY, COUNT_AND_TARGET, NONE.
The canonical field name is snake_case `progress_alert_progress_type`.
For backward-compatibility the API also accepts camelCase `progressAlertProgressType` on input.
enum: [ALL, PERCENTAGE_ONLY, COUNT_ONLY, COUNT_AND_TARGET, NONE]
example: ALL
progress_alert_style:
type: string
description: |
Presentation style of the alert.
Accepted values: DEPARTMENT_COMPARE, COLLECTIVE, SINGLE_DEPARTMENT, NONE.
The canonical field name is snake_case `progress_alert_style`.
For backward-compatibility the API also accepts camelCase `progressAlertStyle` on input.
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}`.
Max length depends on destination: 160 characters for SMS; 1024 characters for EMAIL/SLACK/other.
The canonical field name is snake_case `progress_alert_format`.
For backward-compatibility the API also accepts camelCase `progressAlertFormat` on input.
progress_alert_weekdays:
type: array
description: |
Weekdays on which progress alerts should be sent.
Use one or more of: MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY.
The canonical field name is snake_case `progress_alert_weekdays`.
For backward-compatibility the API also accepts camelCase `progressAlertWeekdays` on input.
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.
Format: `HH:MMZ` or `HH:MM±HH:MM` (24-hour clock with UTC offset). Examples: `14:30Z`, `09:15+02:00`, `18:45-05:00`.
The canonical field name is snake_case `progress_alert_time_of_day`.
For backward-compatibility the API also accepts camelCase `progressAlertTimeOfDay` on input.
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.
If omitted, the daily target is split evenly across selected departments. The canonical field name is snake_case `department_daily_targets`.
For backward-compatibility the API also accepts camelCase `departmentDailyTargets` on input.
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.
Timeframes are clamped to the goal timeframe (never before goal start and never after goal end).
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.
`year` starts at January 1 of the current year or the goal start, whichever is later.
`to_date` starts at the goal start and ends at today (also clamped by goal end).
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.
Includes `all`, `today`, `week`, `month`, `year`, and `to_date` timeframes.
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: []