{"openapi":"3.0.1","info":{"title":"Solvotix Building Automation API","description":"The Solvotix API provides access to building automation data, sensors, gateways,\nand tenant management features.\n\n**Authentication**\n---\nThis API supports two bearer-token authentication methods:\n\n1. **Solvotix API tokens** for integrations and machine-to-machine access. API tokens\n   start with `sat_`, are bound to one tenant, and do not require Firebase.\n2. **Firebase ID tokens** for interactive users signed in with email/password,\n   Google Sign-In, or another configured Firebase provider.\n\nInclude either token in the HTTP header:\n\n```\nAuthorization: Bearer sat_<API_TOKEN>\n```\n\nor\n\n```\nAuthorization: Bearer <FIREBASE_ID_TOKEN>\n```\n\nMost tenant-scoped requests should also include `Tenant: <TENANT_ID>`. A Solvotix API\ntoken can only access the tenant to which it is bound. API tokens can be created, rotated,\nand revoked through `/api/api-users` by an authorized interactive user.\n","contact":{"name":"Solvotix API Support","url":"https://solvotix.org","email":"support@solvotix.org"},"version":"1.0"},"servers":[{"url":"https://backend.solvotix.org","description":"Production Server"},{"url":"http://localhost:8080","description":"Local Development"}],"tags":[{"name":"Thermostat","description":"Endpoints for wall thermostat operations."},{"name":"Gateways","description":"Endpoints for managing **Solvotix Gateways** — including discovery, firmware updates,\nqueue synchronization, and remote node management.\nThese APIs are typically consumed by the Solvotix backend and mobile clients\nfor remote management and monitoring of deployed IoT gateways.\n"},{"name":"Tenants","description":"Endpoints for **managing organizational tenants** in the Solvotix IoT ecosystem.\nEach tenant represents an isolated environment (e.g., a building, facility, or organization)\nwith its own sensors, gateways, and users.\n"},{"name":"Smart Locks","description":"Endpoints for **managing smart locks** — including open/close commands,\ncode management, and configuration updates.\n"},{"name":"Sensors","description":"Endpoints for **managing Solvotix sensors** — including retrieval, configuration,\nBluetooth advertisement control, and command execution via connected gateways.\n"},{"name":"Email Branding","description":"Endpoints for configuring email footer/logo branding per tenant."},{"name":"Automation","description":"Preconfigured building automation scenarios."},{"name":"API users","description":"Tenant-bound machine identities and API tokens"},{"name":"Automated Messages","description":"Predefined messages produced by automation triggers."},{"name":"Guest Portal","description":"Endpoints for guest-facing booking and sensor controls."},{"name":"Bookings","description":"Manage bookings/rooms/categories via the main portal"},{"name":"AI Chat Threads","description":"Manage AI chat threads connected to bookings."},{"name":"Guest Portal Admin","description":"Tenant admin endpoints for guest portal configuration."},{"name":"Modules","description":"Manage module activation per tenant."},{"name":"Temperature Control","description":"Aggregate temperature statistics and control helpers."},{"name":"Node Updates","description":"List node firmware images and start node OTA updates."},{"name":"Relay","description":"Endpoints for relay including open/close and pulse commands."},{"name":"Events","description":"Endpoints for managing Solvotix events, including creation, retrieval, updates, and deletion."},{"name":"Lock Users","description":"Manage smart lock users and their access codes."},{"name":"Users","description":"Endpoints for **managing user accounts** within a Solvotix tenant.\nThese APIs allow tenant administrators to view members, invite new users,\nupdate profile information, and remove accounts securely.\n"},{"name":"Cleaning","description":"Track room cleaning status for cleaning staff."}],"paths":{"/api/users/{id}":{"get":{"tags":["Users"],"summary":"Get user","description":"Retrieves detailed information about a specific user by their unique ID.","operationId":"getUserById","parameters":[{"name":"id","in":"path","description":"User ID","required":true,"schema":{"type":"string"},"example":"u_001"}],"responses":{"200":{"description":"User retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"},"example":{"id":"u_001","email":"jane@solvotix.org","name":"Jane Doe"}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Users"],"summary":"Update user","description":"Updates details for an existing user within the tenant.\nThis can include changing the display name, email, or role (depending on access permissions).\n","operationId":"updateUser","parameters":[{"name":"id","in":"path","description":"User ID","required":true,"schema":{"type":"string"},"example":"u_001"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}},"required":true},"responses":{"200":{"description":"User updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}},"400":{"description":"Invalid update request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Users"],"summary":"Remove user from tenant","description":"Removes a user from the current tenant.\nIf the user no longer belongs to any tenants, their Firebase account may be deleted\nautomatically by the backend for compliance.\n","operationId":"deleteUser","parameters":[{"name":"id","in":"path","description":"User ID to remove","required":true,"schema":{"type":"string"},"example":"u_001"}],"responses":{"200":{"description":"User removed successfully","content":{"application/json":{"schema":{"type":"boolean"}}}},"404":{"description":"User not found or already removed","content":{"application/json":{"schema":{"type":"boolean"}}}}},"security":[{"bearerAuth":[]}]}},"/api/users/pushnotification":{"put":{"tags":["Users"],"summary":"Register a user for push notifications","operationId":"updatePushNotifications","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DevicePushRegistration"}}},"required":true},"responses":{"200":{"description":"OK"}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Users"],"summary":"Register a user for push notifications","operationId":"registerForPushNotifications","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DevicePushRegistration"}}},"required":true},"responses":{"200":{"description":"OK"}},"security":[{"bearerAuth":[]}]}},"/api/users/pushnotification/settings/{userId}":{"get":{"tags":["Users"],"summary":"Get push notification settings for a user","description":"Returns push notification category toggles for the specified user in the current tenant.","operationId":"getPushNotificationSettings","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPushNotificationSettings"}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Users"],"summary":"Update push notification settings for a user","description":"Updates push notification category toggles for the specified user in the current tenant.","operationId":"updatePushNotificationSettings","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPushNotificationSettings"}}},"required":true},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPushNotificationSettings"}}}}},"security":[{"bearerAuth":[]}]}},"/api/users/me":{"get":{"tags":["Users"],"summary":"Get current user","description":"Retrieves the currently authenticated user from the request context.","operationId":"getCurrentUser","responses":{"200":{"description":"Current user retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Users"],"summary":"Save current user","description":"Saves current-user preferences, including the selected UI language.","operationId":"saveCurrentUser","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}},"required":true},"responses":{"200":{"description":"Current user saved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}},"400":{"description":"Invalid update request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}}},"security":[{"bearerAuth":[]}]}},"/api/users/emailnotification":{"get":{"tags":["Users"],"summary":"List email notification toggles for a tenant","description":"Returns all user email notification toggles for the current tenant (derived from headers).","operationId":"getAllEmailNotifications","responses":{"200":{"description":"List returned successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/EmailNotificationRegistration"}},"example":[{"id":"eml_001","userId":"u_001","enabled":true},{"id":"eml_002","userId":"u_002","enabled":false}]}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Users"],"summary":"Update email notification toggle","description":"Updates the email notification preference (on/off). Include the id to overwrite an existing record.","operationId":"updateEmailNotifications","requestBody":{"description":"Existing registration with updated enabled flag","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailNotificationRegistration"},"example":{"id":"eml_001","enabled":false}}},"required":true},"responses":{"200":{"description":"Updated successfully"},"400":{"description":"Invalid payload"},"401":{"description":"Unauthorized"},"404":{"description":"Registration not found"}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Users"],"summary":"Enable/disable email notifications for current user","description":"Toggles email alerts for the authenticated user. Tenant is derived from headers.","operationId":"registerForEmailNotifications","requestBody":{"description":"Preference payload containing tenantId and enabled flag","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailNotificationRegistration"},"example":{"enabled":true}}},"required":true},"responses":{"200":{"description":"Registered/updated successfully"},"400":{"description":"Invalid payload"},"401":{"description":"Unauthorized"}},"security":[{"bearerAuth":[]}]}},"/api/tenants":{"get":{"tags":["Tenants"],"summary":"List all tenants associated with the current user","description":"Returns all tenant objects that the authenticated user has access to.\nIf the user has no existing tenants, a new one may be automatically created\nand assigned to them for convenience.\n","operationId":"getTenants","responses":{"200":{"description":"List of tenants returned successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Tenant"}}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Tenant"}}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Tenants"],"summary":"Update tenant information","description":"Updates a tenant’s name, metadata, or configuration.\nThe authenticated user must be a member of the tenant to perform this action.\n","operationId":"saveTenant","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}},"required":true},"responses":{"200":{"description":"Tenant successfully updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}}},"404":{"description":"Tenant not found or not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Tenants"],"summary":"Create a new tenant","description":"Creates a new tenant and automatically associates it with the currently authenticated user.\nTypically used during onboarding or when adding new organizational sites.\n","operationId":"createTenant","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}},"required":true},"responses":{"200":{"description":"Tenant successfully created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}}},"400":{"description":"Invalid tenant payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Tenants"],"summary":"Delete a tenant","description":"Deletes a tenant from the Solvotix system, removing its association with the current user.\nIf no other users are associated with the tenant, it will be permanently deleted.\n","operationId":"deleteTenant","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}},"required":true},"responses":{"200":{"description":"Tenant deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}}},"404":{"description":"Tenant not found or access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}}}},"security":[{"bearerAuth":[]}]}},"/api/tenants/":{"get":{"tags":["Tenants"],"summary":"List all tenants associated with the current user","description":"Returns all tenant objects that the authenticated user has access to.\nIf the user has no existing tenants, a new one may be automatically created\nand assigned to them for convenience.\n","operationId":"getTenants_1","responses":{"200":{"description":"List of tenants returned successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Tenant"}}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Tenant"}}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Tenants"],"summary":"Update tenant information","description":"Updates a tenant’s name, metadata, or configuration.\nThe authenticated user must be a member of the tenant to perform this action.\n","operationId":"saveTenant_1","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}},"required":true},"responses":{"200":{"description":"Tenant successfully updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}}},"404":{"description":"Tenant not found or not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Tenants"],"summary":"Create a new tenant","description":"Creates a new tenant and automatically associates it with the currently authenticated user.\nTypically used during onboarding or when adding new organizational sites.\n","operationId":"createTenant_1","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}},"required":true},"responses":{"200":{"description":"Tenant successfully created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}}},"400":{"description":"Invalid tenant payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}}}},"security":[{"bearerAuth":[]}]}},"/api/smartlocks/{id}/configuration":{"put":{"tags":["Smart Locks"],"summary":"Set smart lock configuration","description":"Updates master code and toggle configuration for sound and light.","operationId":"setConfiguration","parameters":[{"name":"id","in":"path","description":"Smart lock sensor ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SmartLockConfigurationRequest"}}},"required":true},"responses":{"200":{"description":"Configuration command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid sensor or payload","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Sensor not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/smartlocks/configuration/global":{"get":{"tags":["Smart Locks"],"summary":"Get global smart lock configuration","description":"Returns tenant-level configuration applied to all smart locks.","operationId":"getGlobalConfiguration","responses":{"200":{"description":"Configuration returned","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SmartLockGlobalConfiguration"}}}},"400":{"description":"Missing tenant context","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SmartLockGlobalConfiguration"}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Smart Locks"],"summary":"Save global smart lock configuration","description":"Updates tenant-level configuration applied to all smart locks.","operationId":"saveGlobalConfiguration","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SmartLockGlobalConfiguration"}}},"required":true},"responses":{"200":{"description":"Configuration saved","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SmartLockGlobalConfiguration"}}}},"400":{"description":"Invalid configuration","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SmartLockGlobalConfiguration"}}}}},"security":[{"bearerAuth":[]}]}},"/api/sensor/{id}":{"get":{"tags":["Sensors"],"summary":"Get sensor by ID","description":"Fetches details of a specific sensor by its unique ID.","operationId":"getSensorById","parameters":[{"name":"id","in":"path","description":"Unique sensor ID","required":true,"schema":{"type":"string"},"example":"sensor_AA11BB22"}],"responses":{"200":{"description":"Sensor found and returned successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sensor"}}}},"404":{"description":"Sensor not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sensor"}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Sensors"],"summary":"Update a sensor","description":"Updates metadata or configuration of a given sensor.\n\nThe body must contain a valid Sensor object. Returns the updated sensor entity on success.\n","operationId":"updateSensor","parameters":[{"name":"id","in":"path","description":"Sensor ID to update","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sensor"}}},"required":true},"responses":{"200":{"description":"Sensor updated successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/Sensor"}}}},"404":{"description":"Sensor not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/Sensor"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Sensors"],"summary":"Delete a sensor","description":"Removes a sensor from the Solvotix system database.","operationId":"deleteSensor","parameters":[{"name":"id","in":"path","description":"Sensor ID to delete","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Sensor deleted successfully"},"404":{"description":"Sensor not found"}},"security":[{"bearerAuth":[]}]}},"/api/sensor/{id}/pairings":{"get":{"tags":["Sensors"],"summary":"Get persisted paired devices for a sensor","description":"Returns the currently stored pairing list from the backend sensor document.","operationId":"getPairings","parameters":[{"name":"id","in":"path","description":"Sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pairings returned","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SensorPairedDevice"}}}}},"404":{"description":"Sensor not found","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SensorPairedDevice"}}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Sensors"],"summary":"Replace paired devices on a sensor","description":"Queues a pairing write for the sensor and persists the requested paired device list locally as pending.","operationId":"updatePairings","parameters":[{"name":"id","in":"path","description":"Sensor ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SensorPairingRequest"}}},"required":true},"responses":{"200":{"description":"Pairing write queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid sensor or pair payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Sensor not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/sensor/temperature-control/configuration":{"get":{"tags":["Sensors"],"summary":"Get temperature control configuration","description":"Retrieves the per-tenant temperature control configuration, creating it if missing.","operationId":"getTemperatureControlConfiguration","responses":{"200":{"description":"Configuration returned successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemperatureControlConfiguration"}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Sensors"],"summary":"Save temperature control configuration","description":"Creates or updates the per-tenant temperature control configuration.","operationId":"saveTemperatureControlConfiguration","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemperatureControlConfiguration"}}},"required":true},"responses":{"200":{"description":"Configuration saved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemperatureControlConfiguration"}}}}},"security":[{"bearerAuth":[]}]}},"/api/sensor/groups":{"put":{"tags":["Sensors"],"summary":"Create or update a sensor group","description":"Creates a new group when id is missing, otherwise updates the existing group.","operationId":"saveSensorGroup","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SensorGroup"}}},"required":true},"responses":{"200":{"description":"Group saved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SensorGroup"}}}}},"security":[{"bearerAuth":[]}]}},"/api/relay/{id}/default-milliseconds":{"put":{"tags":["Relay"],"summary":"Set relay default milliseconds","description":"Sets `relay.configuration.relay_open_ms` used by `POST /open` and `POST /pulse-open`.","operationId":"setDefaultMilliseconds","parameters":[{"name":"id","in":"path","description":"Relay ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RelayConfigurationRequest"}}},"required":true},"responses":{"200":{"description":"Default milliseconds saved","content":{"*/*":{"schema":{"$ref":"#/components/schemas/Sensor"}}}},"400":{"description":"Invalid relay or payload","content":{"*/*":{"schema":{"$ref":"#/components/schemas/Sensor"}}}},"404":{"description":"Relay not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/Sensor"}}}}},"security":[{"bearerAuth":[]}]}},"/api/modules/settings":{"get":{"tags":["Modules"],"summary":"Get tenant module settings","description":"Returns automation and integration modules with activation state for the current tenant.","operationId":"getSettings","responses":{"200":{"description":"Module settings loaded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantModuleSettingsResponse"}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Modules"],"summary":"Update tenant module settings","description":"Persists module activation per tenant. Unknown modules are ignored.","operationId":"updateSettings","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantModuleSettingsRequest"}}},"required":true},"responses":{"200":{"description":"Module settings saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantModuleSettingsResponse"}}}}},"security":[{"bearerAuth":[]}]}},"/api/lockusers/{id}":{"get":{"tags":["Lock Users"],"summary":"Get lock user","operationId":"getById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Lock user returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}}},"404":{"description":"Lock user not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Lock Users"],"summary":"Update lock user","description":"Updates a lock user and syncs changed codes/sensors to assigned lock sensors.","operationId":"update","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}},"required":true},"responses":{"200":{"description":"Lock user updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}}},"400":{"description":"Invalid payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}}},"404":{"description":"Lock user not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Lock Users"],"summary":"Delete lock user","description":"Deletes a lock user and queues removal of no-longer-referenced sensor/code pairs.","operationId":"delete","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Lock user deleted"},"404":{"description":"Lock user not found"}},"security":[{"bearerAuth":[]}]}},"/api/lock-users/{id}":{"get":{"tags":["Lock Users"],"summary":"Get lock user","operationId":"getById_1","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Lock user returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}}},"404":{"description":"Lock user not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Lock Users"],"summary":"Update lock user","description":"Updates a lock user and syncs changed codes/sensors to assigned lock sensors.","operationId":"update_1","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}},"required":true},"responses":{"200":{"description":"Lock user updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}}},"400":{"description":"Invalid payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}}},"404":{"description":"Lock user not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Lock Users"],"summary":"Delete lock user","description":"Deletes a lock user and queues removal of no-longer-referenced sensor/code pairs.","operationId":"delete_1","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Lock user deleted"},"404":{"description":"Lock user not found"}},"security":[{"bearerAuth":[]}]}},"/api/guest-portal-admin/settings":{"get":{"tags":["Guest Portal Admin"],"summary":"Get guest portal settings for current tenant","operationId":"getGuestPortalSettings","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestPortalSettings"}}}}}},"put":{"tags":["Guest Portal Admin"],"summary":"Update guest portal settings for current tenant","operationId":"updateGuestPortalSettings","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestPortalSettings"}}},"required":true},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestPortalSettings"}}}}}}},"/api/guest-portal-admin/languages/activated":{"get":{"tags":["Guest Portal Admin"],"summary":"Get activated guest portal languages for current tenant","operationId":"getActivatedLanguages","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GuestPortalLanguageOption"}}}}}}},"put":{"tags":["Guest Portal Admin"],"summary":"Update activated guest portal languages for current tenant","operationId":"updateActivatedLanguages","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestPortalActivatedLanguagesRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GuestPortalLanguageOption"}}}}}}}},"/api/guest-portal-admin/direction-routes/{routeId}":{"get":{"tags":["Guest Portal Admin"],"summary":"Get a guest direction route","operationId":"getDirectionRoute","parameters":[{"name":"routeId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestPortalDirectionRoute"}}}}}},"put":{"tags":["Guest Portal Admin"],"summary":"Update a guest direction route","operationId":"updateDirectionRoute","parameters":[{"name":"routeId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestPortalDirectionRoute"}}},"required":true},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestPortalDirectionRoute"}}}}}},"delete":{"tags":["Guest Portal Admin"],"summary":"Delete a guest direction route","operationId":"deleteDirectionRoute","parameters":[{"name":"routeId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/gateways/{id}":{"get":{"tags":["Gateways"],"summary":"Get a gateway by ID","description":"Retrieve detailed information about a specific gateway by its unique identifier.","operationId":"getById_2","parameters":[{"name":"id","in":"path","description":"Unique identifier of the gateway","required":true,"schema":{"type":"string"},"example":"gw_64F2D9B"}],"responses":{"200":{"description":"Gateway found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/Gateway"}}}},"404":{"description":"Gateway not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/Gateway"}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Gateways"],"summary":"Update an existing gateway","description":"Updates the configuration or metadata of an existing gateway.","operationId":"update_2","parameters":[{"name":"id","in":"path","description":"Gateway ID to update","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Gateway"}}},"required":true},"responses":{"200":{"description":"Gateway updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Gateway"}}}},"404":{"description":"Gateway not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Gateway"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Gateways"],"summary":"Delete a gateway","description":"Permanently removes a gateway from the system.","operationId":"deleteGateway","parameters":[{"name":"id","in":"path","description":"Unique gateway ID to delete","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Gateway deleted successfully"},"404":{"description":"Gateway not found"}},"security":[{"bearerAuth":[]}]}},"/api/events/{id}":{"get":{"tags":["Events"],"summary":"Get event by ID","description":"Retrieves a single event by its unique identifier.\nReturns 404 if the event does not exist.\n","operationId":"getEventById","parameters":[{"name":"id","in":"path","description":"The unique ID of the event","required":true,"schema":{"type":"string"},"example":"652af18e4b1d882f9c923d7a"}],"responses":{"200":{"description":"Event found and returned successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"}}}},"404":{"description":"Event not found"}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Events"],"summary":"Update existing event","description":"Updates an existing event by ID.\nThe provided `Event` body replaces existing data for that event.\n","operationId":"updateEvent","parameters":[{"name":"id","in":"path","description":"The unique ID of the event to update","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"}}},"required":true},"responses":{"200":{"description":"Event updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"}}}},"404":{"description":"Event not found"}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Events"],"summary":"Delete an event","description":"Deletes an event permanently from the Solvotix database.\nUse with caution — this operation cannot be undone.\n","operationId":"deleteEvent","parameters":[{"name":"id","in":"path","description":"The unique ID of the event to delete","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Event deleted successfully"},"404":{"description":"Event not found"}},"security":[{"bearerAuth":[]}]}},"/api/email-branding":{"get":{"tags":["Email Branding"],"summary":"Get email branding settings","description":"Returns the current tenant's email branding settings (creates defaults if missing).","operationId":"getSettings_1","responses":{"200":{"description":"Settings returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailBrandingSettings"}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Email Branding"],"summary":"Update email branding settings","description":"Updates the current tenant's email branding settings.","operationId":"saveSettings","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailBrandingSettings"}}},"required":true},"responses":{"200":{"description":"Settings saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailBrandingSettings"}}}}},"security":[{"bearerAuth":[]}]}},"/api/cleaning/settings":{"get":{"tags":["Cleaning"],"summary":"Get cleaning program settings","operationId":"getCleaningProgramSettings","responses":{"200":{"description":"Cleaning settings returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleaningProgramSettings"}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Cleaning"],"summary":"Update cleaning program settings","operationId":"updateCleaningProgramSettings","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleaningProgramSettings"}}},"required":true},"responses":{"200":{"description":"Cleaning settings updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleaningProgramSettings"}}}}},"security":[{"bearerAuth":[]}]}},"/api/cleaning/rooms/{roomId}":{"get":{"tags":["Cleaning"],"summary":"Get room cleaning status","operationId":"getRoom_1","parameters":[{"name":"roomId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Cleaning status returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleaningRoom"}}}},"404":{"description":"Room not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleaningRoom"}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Cleaning"],"summary":"Update room cleaning status","description":"Allows cleaning staff to mark a room as cleaned or reset it back to not cleaned.","operationId":"updateCleaningStatus","parameters":[{"name":"roomId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleaningStatusUpdateRequest"}}},"required":true},"responses":{"200":{"description":"Cleaning status updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleaningRoom"}}}},"404":{"description":"Room not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleaningRoom"}}}}},"security":[{"bearerAuth":[]}]}},"/api/bookings/rooms/{roomId}":{"put":{"tags":["Bookings"],"summary":"Update room","operationId":"updateRoom","parameters":[{"name":"roomId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rooms"}}},"required":true},"responses":{"200":{"description":"Updated room","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rooms"}}}},"404":{"description":"Room not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rooms"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Bookings"],"summary":"Delete room","operationId":"deleteRoom","parameters":[{"name":"roomId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"}},"security":[{"bearerAuth":[]}]}},"/api/bookings/categories/{categoryId}":{"put":{"tags":["Bookings"],"summary":"Update category","operationId":"updateCategory","parameters":[{"name":"categoryId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Categories"}}},"required":true},"responses":{"200":{"description":"Updated category","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Categories"}}}},"404":{"description":"Category not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Categories"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Bookings"],"summary":"Delete category","operationId":"deleteCategory","parameters":[{"name":"categoryId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"}},"security":[{"bearerAuth":[]}]}},"/api/bookings/bookings/{bookingId}":{"put":{"tags":["Bookings"],"summary":"Update booking","description":"Updates a booking by bookingId.","operationId":"updateBooking","parameters":[{"name":"bookingId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}},"required":true},"responses":{"200":{"description":"Updated booking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"404":{"description":"Booking not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Bookings"],"summary":"Delete booking","description":"Deletes a booking by bookingId.","operationId":"deleteBooking","parameters":[{"name":"bookingId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"}},"security":[{"bearerAuth":[]}]}},"/api/automation/messages/triggers/{id}":{"put":{"tags":["Automated Messages"],"summary":"Update trigger","description":"Updates an automation trigger with new recipient, channel, or content settings.","operationId":"updateTrigger","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationTriggerRequest"}}},"required":true},"responses":{"200":{"description":"Trigger updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationTrigger"}}}},"404":{"description":"Trigger not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationTrigger"}}}},"400":{"description":"Invalid trigger request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationTrigger"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Automated Messages"],"summary":"Delete trigger","description":"Deletes an automation trigger by id.","operationId":"deleteTrigger","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"},"404":{"description":"Trigger not found"}},"security":[{"bearerAuth":[]}]}},"/api/ai/settings":{"get":{"tags":["AI Chat Threads"],"summary":"Get AI settings","description":"Returns tenant-level AI settings including free-text instructions.","operationId":"getSettings_2","responses":{"200":{"description":"Settings returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AISettings"}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["AI Chat Threads"],"summary":"Save AI settings","description":"Stores tenant-level AI settings including free-text instructions.","operationId":"updateSettings_1","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AISettings"}}},"required":true},"responses":{"200":{"description":"Settings saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AISettings"}}}},"400":{"description":"Invalid settings payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AISettings"}}}}},"security":[{"bearerAuth":[]}]}},"/api/users":{"get":{"tags":["Users"],"summary":"List users","description":"Returns a list of all users associated with the current tenant.\nThis endpoint is typically used in admin dashboards or management panels.\n","operationId":"getAllUsers","responses":{"200":{"description":"List of users returned successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/User"}},"example":[{"id":"u_001","email":"jane@solvotix.org","name":"Jane Doe"},{"id":"u_002","email":"john@solvotix.org","name":"John Smith"}]}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/User"}}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/User"}}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Users"],"summary":"Invite a new user","description":"Creates and invites a new user to the tenant via email.\nThe user receives an activation link generated by Firebase Authentication.\n\n**Workflow:**\n1. A tenant admin calls this endpoint with an email and name.\n2. Firebase creates the user and generates a password-setup link.\n3. Solvotix sends a branded invitation email to the new user.\n4. The new user joins the tenant upon activation.\n","operationId":"addUser","requestBody":{"description":"Payload with name and email for the new user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"},"example":{"email":"new@solvotix.org","name":"New User"}}},"required":true},"responses":{"200":{"description":"User invited successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"},"example":{"id":"u_010","email":"new@solvotix.org","name":"New User"}}}},"400":{"description":"Invalid request payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}}},"security":[{"bearerAuth":[]}]}},"/api/thermostat/{id}/wifi":{"post":{"tags":["Thermostat"],"summary":"Set thermostat Wi-Fi credentials","description":"Queues wall thermostat operation subaction 1 (set Wi-Fi) using ssid and password.","operationId":"setWifi","parameters":[{"name":"id","in":"path","description":"Wall thermostat sensor ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThermostatWifiRequest"}}},"required":true},"responses":{"200":{"description":"Wi-Fi command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid thermostat or payload","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Thermostat not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/thermostat/{id}/target-temperature":{"post":{"tags":["Thermostat"],"summary":"Set target temperature","description":"Queues wall thermostat operation subaction 2 (set target temperature) using a decimal temperature with two decimals.","operationId":"setTargetTemperature","parameters":[{"name":"id","in":"path","description":"Wall thermostat sensor ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThermostatTargetTemperatureRequest"}}},"required":true},"responses":{"200":{"description":"Target temperature command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid thermostat or payload","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Thermostat not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/thermostat/{id}/restart":{"post":{"tags":["Thermostat"],"summary":"Restart thermostat","description":"Queues wall thermostat operation subaction 5 (restart).","operationId":"restart","parameters":[{"name":"id","in":"path","description":"Wall thermostat sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Restart command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid thermostat","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Thermostat not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/thermostat/{id}/receive-debug-data":{"post":{"tags":["Thermostat"],"summary":"Receive thermostat debug data","description":"Queues wall thermostat operation subaction 7 (receive debug data). The thermostat is expected to open a TCP connection to port 30100 and stream terminal output.","operationId":"receiveDebugData","parameters":[{"name":"id","in":"path","description":"Wall thermostat sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Receive debug data command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid thermostat","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Thermostat not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/thermostat/{id}/on":{"post":{"tags":["Thermostat"],"summary":"Turn thermostat on","description":"Queues wall thermostat operation subaction 3 (turn on).","operationId":"turnOn","parameters":[{"name":"id","in":"path","description":"Wall thermostat sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Turn on command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid thermostat","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Thermostat not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/thermostat/{id}/off":{"post":{"tags":["Thermostat"],"summary":"Turn thermostat off","description":"Queues wall thermostat operation subaction 4 (turn off).","operationId":"turnOff","parameters":[{"name":"id","in":"path","description":"Wall thermostat sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Turn off command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid thermostat","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Thermostat not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/thermostat/{id}/delete-wifi":{"post":{"tags":["Thermostat"],"summary":"Delete thermostat Wi-Fi credentials","description":"Queues wall thermostat operation subaction 6 (delete Wi-Fi).","operationId":"deleteWifi","parameters":[{"name":"id","in":"path","description":"Wall thermostat sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Delete Wi-Fi command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid thermostat","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Thermostat not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/smartlocks/{id}/pulse-open":{"post":{"tags":["Smart Locks"],"summary":"Pulse open a smart lock","description":"Triggers a short open pulse on the smart lock relay.","operationId":"pulseOpen","parameters":[{"name":"id","in":"path","description":"Smart lock sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pulse open command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid sensor or payload","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Sensor not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/smartlocks/{id}/open":{"post":{"tags":["Smart Locks"],"summary":"Open a smart lock","description":"Sends a persistent open command to the smart lock.","operationId":"open","parameters":[{"name":"id","in":"path","description":"Smart lock sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Open command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid sensor or payload","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Sensor not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/smartlocks/{id}/codes/resend":{"post":{"tags":["Smart Locks"],"summary":"Resend all access codes","description":"Deletes all codes on the lock and re-sends every stored access code.","operationId":"resendCodes","parameters":[{"name":"id","in":"path","description":"Smart lock sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Resend commands queued","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MessageFrame"}}}}},"400":{"description":"Invalid sensor","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MessageFrame"}}}}},"404":{"description":"Sensor not found","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MessageFrame"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/smartlocks/{id}/codes/remove":{"post":{"tags":["Smart Locks"],"summary":"Remove access codes","description":"Removes one or more access codes from the smart lock.","operationId":"removeCodes","parameters":[{"name":"id","in":"path","description":"Smart lock sensor ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SmartLockCodesRequest"}}},"required":true},"responses":{"200":{"description":"Remove codes command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid sensor or payload","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Sensor not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/smartlocks/{id}/codes/add":{"post":{"tags":["Smart Locks"],"summary":"Add access codes","description":"Adds one or more access codes to the smart lock.","operationId":"addCodes","parameters":[{"name":"id","in":"path","description":"Smart lock sensor ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SmartLockCodesRequest"}}},"required":true},"responses":{"200":{"description":"Add codes command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid sensor or payload","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Sensor not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/smartlocks/{id}/close":{"post":{"tags":["Smart Locks"],"summary":"Close a smart lock","description":"Sends a close command to the smart lock.","operationId":"close","parameters":[{"name":"id","in":"path","description":"Smart lock sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Close command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid sensor or payload","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Sensor not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/sensor/{id}/restart":{"post":{"tags":["Sensors"],"summary":"Restart sensor","description":"Queues a restart command for a sensor using node action 35.","operationId":"restart_1","parameters":[{"name":"id","in":"path","description":"Sensor ID","required":true,"schema":{"type":"string"},"example":"sensor_849FAD3"}],"responses":{"200":{"description":"Restart successfully queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Sensor not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/sensor/{id}/restart-mode/{enabled}":{"post":{"tags":["Sensors"],"summary":"Toggle restart mode","description":"Queues restart mode toggle on a sensor (`true` = on, `false` = off) and logs a Solvotix event.","operationId":"toggleRestartMode","parameters":[{"name":"id","in":"path","description":"Sensor ID","required":true,"schema":{"type":"string"},"example":"sensor_849FAD3"},{"name":"enabled","in":"path","description":"Restart mode state (`true` to enable, `false` to disable)","required":true,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Restart mode toggle successfully queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Sensor not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/sensor/{id}/pairings/fetch":{"post":{"tags":["Sensors"],"summary":"Queue a pairing read from a sensor","description":"Asks the device to return its currently persisted paired device list.","operationId":"fetchPairings","parameters":[{"name":"id","in":"path","description":"Sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pairing read queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid sensor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Sensor not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/sensor/{id}/claim/{name}":{"post":{"tags":["Sensors"],"summary":"Claim a sensor","description":"Claims a sensor under the current tenant using the sensor UUID string and a human-readable name.\nIf the sensor is already claimed by another tenant, the request is rejected.\n","operationId":"claimSensor","parameters":[{"name":"id","in":"path","description":"Sensor UUID (payload.getUUIDString())","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","description":"Human-readable sensor name","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Sensor claimed successfully","content":{"*/*":{"schema":{"type":"object"}}}},"400":{"description":"Missing sensor id or name","content":{"*/*":{"schema":{"type":"object"}}}},"409":{"description":"Sensor already claimed by another tenant","content":{"*/*":{"schema":{"type":"object"}}}}},"security":[{"bearerAuth":[]}]}},"/api/sensor/{id}/advprofile/{profile}":{"post":{"tags":["Sensors"],"summary":"Change Bluetooth advertisement profile","description":"Updates the Bluetooth Low Energy advertisement profile of a specific sensor.\n\nSupported profile IDs:\n0=UltraSave, 1=UltraSaveLR, 2=Save, 3=SaveLR, 4=Normal, 5=NormalLR,\n6=Responsive, 7=ResponsiveLR, 8=MidResponsive, 9=MidResponsiveLR.\n","operationId":"changeProfile","parameters":[{"name":"id","in":"path","description":"Sensor ID","required":true,"schema":{"type":"string"},"example":"sensor_849FAD3"},{"name":"profile","in":"path","description":"Profile ID (0=UltraSave, 1=UltraSaveLR, 2=Save, 3=SaveLR, 4=Normal, 5=NormalLR, 6=Responsive, 7=ResponsiveLR, 8=MidResponsive, 9=MidResponsiveLR)","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"Profile change successfully queued","content":{"*/*":{"schema":{"type":"string"}}}},"404":{"description":"Sensor not found","content":{"*/*":{"schema":{"type":"string"}}}}},"security":[{"bearerAuth":[]}]}},"/api/sensor/{id}/action":{"post":{"tags":["Sensors"],"summary":"Perform an action on a sensor","description":"Sends a control command to a specific sensor (for example: `test`, `restart_node`, or `update_node`).\n\nFor a relay, action `40` opens it until the requested CF pulse count has been reached.\nSupply the unsigned 32-bit pulse count as a decimal string in `value`; `0` cancels\nan active countdown and closes the relay.\n\nThe backend dispatches this action via the associated gateway using the Solvotix protocol.\n","operationId":"sensorAction","parameters":[{"name":"id","in":"path","description":"Unique ID of the sensor","required":true,"schema":{"type":"string"},"example":"sensor_849FAD3"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SensorActionRequest"}}},"required":true},"responses":{"200":{"description":"Action successfully queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Sensor not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid or unsupported action request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/relay/{id}/pulse/seconds/{seconds}":{"post":{"tags":["Relay"],"summary":"Pulse relay in seconds","description":"Queues a relay pulse command with second duration.","operationId":"pulseSeconds","parameters":[{"name":"id","in":"path","description":"Relay ID","required":true,"schema":{"type":"string"}},{"name":"seconds","in":"path","description":"Pulse duration in seconds (0-256)","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"Pulse command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid relay or duration","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Relay not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/relay/{id}/pulse/ms/{milliseconds}":{"post":{"tags":["Relay"],"summary":"Pulse relay in milliseconds","description":"Queues a relay pulse command with millisecond duration.","operationId":"pulseMilliseconds","parameters":[{"name":"id","in":"path","description":"Relay ID","required":true,"schema":{"type":"string"}},{"name":"milliseconds","in":"path","description":"Pulse duration in milliseconds (0-256)","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"Pulse command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid relay or duration","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Relay not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/relay/{id}/pulse/minutes/{minutes}":{"post":{"tags":["Relay"],"summary":"Pulse relay in minutes","description":"Queues a relay pulse command with minute duration.","operationId":"pulseMinutes","parameters":[{"name":"id","in":"path","description":"Relay ID","required":true,"schema":{"type":"string"}},{"name":"minutes","in":"path","description":"Pulse duration in minutes (0-256)","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"Pulse command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid relay or duration","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Relay not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/relay/{id}/pulse-open":{"post":{"tags":["Relay"],"summary":"Pulse open relay (auto unit)","description":"Opens relay based on `relay.configuration.relay_open_ms` and automatically chooses\nms/seconds/minutes command. Defaults to 5000ms (5 seconds) when not configured.\n","operationId":"pulseOpen_1","parameters":[{"name":"id","in":"path","description":"Relay ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pulse command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid relay or duration","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Relay not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/relay/{id}/open":{"post":{"tags":["Relay"],"summary":"Open relay using configured milliseconds","description":"Uses the relay configuration key `relay_open_ms` and sends a millisecond pulse command.","operationId":"open_1","parameters":[{"name":"id","in":"path","description":"Relay ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Open command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid relay","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Relay not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/relay/{id}/open-until-closed":{"post":{"tags":["Relay"],"summary":"Open relay until closed","description":"Queues relay open action (without pulse duration).","operationId":"openUntilClosed","parameters":[{"name":"id","in":"path","description":"Relay ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Open until closed command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid relay","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Relay not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/relay/{id}/close":{"post":{"tags":["Relay"],"summary":"Close relay","description":"Queues relay close action.","operationId":"close_1","parameters":[{"name":"id","in":"path","description":"Relay ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Close command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid relay","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Relay not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/node-updates/sensors/{sensorId}":{"post":{"tags":["Node Updates"],"summary":"Start a node firmware update","operationId":"startUpdate","parameters":[{"name":"sensorId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeUpdateRequest"}}}},"responses":{"202":{"description":"Node update queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/NodeUpdateStartResponse"}}}},"400":{"description":"Invalid request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/NodeUpdateStartResponse"}}}},"409":{"description":"Update already running","content":{"*/*":{"schema":{"$ref":"#/components/schemas/NodeUpdateStartResponse"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Node Updates"],"summary":"Abort a node firmware update","description":"Deletes all queued OTA update messages for the given sensor and resets its update state.","operationId":"abortUpdate","parameters":[{"name":"sensorId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Update aborted, returns number of removed messages","content":{"*/*":{"schema":{"type":"integer","format":"int32"}}}},"400":{"description":"Invalid sensor ID","content":{"*/*":{"schema":{"type":"integer","format":"int32"}}}},"409":{"description":"No active update for this sensor","content":{"*/*":{"schema":{"type":"integer","format":"int32"}}}}},"security":[{"bearerAuth":[]}]}},"/api/lock-users":{"get":{"tags":["Lock Users"],"summary":"List lock users","operationId":"list","responses":{"200":{"description":"List of lock users","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/LockUser"}}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Lock Users"],"summary":"Create lock user","description":"Creates a lock user and queues its codes for all assigned lock sensors.","operationId":"create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}},"required":true},"responses":{"200":{"description":"Lock user created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}}},"400":{"description":"Invalid payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}}}},"security":[{"bearerAuth":[]}]}},"/api/lockusers":{"get":{"tags":["Lock Users"],"summary":"List lock users","operationId":"list_1","responses":{"200":{"description":"List of lock users","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/LockUser"}}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Lock Users"],"summary":"Create lock user","description":"Creates a lock user and queues its codes for all assigned lock sensors.","operationId":"create_1","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}},"required":true},"responses":{"200":{"description":"Lock user created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}}},"400":{"description":"Invalid payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUser"}}}}},"security":[{"bearerAuth":[]}]}},"/api/guest-portal/bookings/{bookingId}/sensors/{sensorId}/action":{"post":{"tags":["Guest Portal"],"summary":"Perform an action on a sensor for a guest booking","operationId":"sensoraction","parameters":[{"name":"tenantId","in":"query","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}},{"name":"sensorId","in":"path","description":"Sensor ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SensorActionRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}}}},"/api/guest-portal/bookings/{bookingId}/checkout":{"post":{"tags":["Guest Portal"],"summary":"Notify staff that a guest is checking out","operationId":"checkout","parameters":[{"name":"tenantId","in":"query","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestCheckoutResponse"}}}}}}},"/api/guest-portal/bookings/{bookingId}/ai/threads":{"get":{"tags":["Guest Portal"],"summary":"List AI chat threads for a booking","operationId":"listAIThreads","parameters":[{"name":"tenantId","in":"query","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AIChatThread"}}}}}}},"post":{"tags":["Guest Portal"],"summary":"Start AI chat thread for a booking","operationId":"startAIThread","parameters":[{"name":"tenantId","in":"query","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIChatThread"}}}}}}},"/api/guest-portal-admin/direction-routes":{"get":{"tags":["Guest Portal Admin"],"summary":"List guest direction routes","operationId":"listDirectionRoutes","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GuestPortalDirectionRoute"}}}}}}},"post":{"tags":["Guest Portal Admin"],"summary":"Create a guest direction route","operationId":"createDirectionRoute","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestPortalDirectionRoute"}}},"required":true},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestPortalDirectionRoute"}}}}}}},"/api/guest-portal-admin/direction-images":{"get":{"tags":["Guest Portal Admin"],"summary":"List guest direction image metadata","operationId":"listDirectionImages","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GuestPortalDirectionImage"}}}}}}},"post":{"tags":["Guest Portal Admin"],"summary":"Upload a Base64 guest direction image","operationId":"createDirectionImage","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestPortalDirectionImage"}}},"required":true},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestPortalDirectionImage"}}}}}}},"/api/gateways/{id}/{name}":{"post":{"tags":["Gateways"],"summary":"Claim a new gateway","description":"Registers a new gateway with a specified name under the current user's account.","operationId":"claimGateway","parameters":[{"name":"id","in":"path","description":"Gateway ID","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","description":"Human-readable gateway name","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Gateway"}}}}},"security":[{"bearerAuth":[]}]}},"/api/gateways/{gatewayId}/relay/restart":{"post":{"tags":["Gateways"],"summary":"Restart relay","description":"Commands the gateway to toggle the relay for ~200ms (status 25).","operationId":"restartRelay","parameters":[{"name":"gatewayId","in":"path","description":"Gateway ID","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Command accepted"},"404":{"description":"Gateway not found"}},"security":[{"bearerAuth":[]}]}},"/api/gateways/{gatewayId}/relay/on":{"post":{"tags":["Gateways"],"summary":"Turn relay on","description":"Instructs the gateway to close the onboard relay (status 23).","operationId":"turnRelayOn","parameters":[{"name":"gatewayId","in":"path","description":"Gateway ID","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Command accepted"},"404":{"description":"Gateway not found"}},"security":[{"bearerAuth":[]}]}},"/api/gateways/{gatewayId}/relay/off":{"post":{"tags":["Gateways"],"summary":"Turn relay off","description":"Instructs the gateway to open the onboard relay (status 24).","operationId":"turnRelayOff","parameters":[{"name":"gatewayId","in":"path","description":"Gateway ID","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Command accepted"},"404":{"description":"Gateway not found"}},"security":[{"bearerAuth":[]}]}},"/api/gateways/{gatewayId}/receive-debug-data":{"post":{"tags":["Gateways"],"summary":"Receive gateway debug data","description":"Instructs the gateway to open a TCP connection to port 30100 and stream terminal output (status 29).","operationId":"receiveDebugData_1","parameters":[{"name":"gatewayId","in":"path","description":"Gateway ID","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Command accepted"},"404":{"description":"Gateway not found"}},"security":[{"bearerAuth":[]}]}},"/api/gateways/{gatewayId}/nodes/{nodeId}/connection/open":{"post":{"tags":["Gateways"],"summary":"Open a connection to a node","description":"Instructs the gateway to open a BLE session for the provided node. The backend will then wait for the gateway to report status=18/20.","operationId":"openConnection","parameters":[{"name":"gatewayId","in":"path","description":"Gateway ID managing the node","required":true,"schema":{"type":"string"}},{"name":"nodeId","in":"path","description":"Node UUID that should be connected","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"bearerAuth":[]}]}},"/api/gateways/{gatewayId}/nodes/{nodeId}/connection/close":{"post":{"tags":["Gateways"],"summary":"Close an active node connection","description":"Tells the gateway to drop any concurrent connection for the given node (status=19).","operationId":"closeConnection","parameters":[{"name":"gatewayId","in":"path","description":"Gateway ID managing the node","required":true,"schema":{"type":"string"}},{"name":"nodeId","in":"path","description":"Node UUID whose connection should be closed","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"bearerAuth":[]}]}},"/api/events":{"get":{"tags":["Events"],"summary":"Get all events","description":"Retrieves a list of all Solvotix events in the system.\nThis endpoint returns both active and historical events, typically used by dashboards or analytics.\n","operationId":"getAllEvents","responses":{"200":{"description":"List of all events retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SolvotixEvent"}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Events"],"summary":"Create a new event","description":"Creates and stores a new event in the Solvotix event database.\nThe request body must contain a valid `Event` object with required metadata.\n","operationId":"create_2","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"}}},"required":true},"responses":{"200":{"description":"Event successfully created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"}}}},"400":{"description":"Invalid event data provided"}},"security":[{"bearerAuth":[]}]}},"/api/cleaning/rooms/{roomId}/cleaned":{"post":{"tags":["Cleaning"],"summary":"Mark room as cleaned","operationId":"markRoomAsCleaned","parameters":[{"name":"roomId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Room marked as cleaned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleaningRoom"}}}},"404":{"description":"Room not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleaningRoom"}}}}},"security":[{"bearerAuth":[]}]}},"/api/bookings/rooms":{"get":{"tags":["Bookings"],"summary":"List rooms","operationId":"listRooms_1","responses":{"200":{"description":"List of rooms","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Rooms"}}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Bookings"],"summary":"Create room","operationId":"createRoom","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rooms"}}},"required":true},"responses":{"200":{"description":"Created room","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rooms"}}}}},"security":[{"bearerAuth":[]}]}},"/api/bookings/rooms/{roomId}/codes":{"post":{"tags":["Bookings"],"summary":"Add room codes","description":"Adds one or more explicit 4-7 digit codes to a room and ensures they are present on all room locks.","operationId":"addRoomCodes","parameters":[{"name":"roomId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SmartLockCodesRequest"}}},"required":true},"responses":{"200":{"description":"Updated room","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rooms"}}}},"400":{"description":"Invalid code payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rooms"}}}},"404":{"description":"Room not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rooms"}}}},"409":{"description":"Code already exists in another room","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rooms"}}}}},"security":[{"bearerAuth":[]}]}},"/api/bookings/rooms/{roomId}/codes/{code}/refresh":{"post":{"tags":["Bookings"],"summary":"Refresh room code","description":"Replaces the given room code with a new random code.","operationId":"refreshRoomCode","parameters":[{"name":"roomId","in":"path","required":true,"schema":{"type":"string"}},{"name":"code","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"New code returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomCodeResponse"}}}},"404":{"description":"Room not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomCodeResponse"}}}}},"security":[{"bearerAuth":[]}]}},"/api/bookings/rooms/{roomId}/codes/random":{"post":{"tags":["Bookings"],"summary":"Add random room code","description":"Generates a random 4-7 digit code and adds it to the room. Codes are unique across all rooms.","operationId":"addRandomRoomCode","parameters":[{"name":"roomId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Updated room","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rooms"}}}},"404":{"description":"Room not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rooms"}}}}},"security":[{"bearerAuth":[]}]}},"/api/bookings/rooms/codes/resync":{"post":{"tags":["Bookings"],"summary":"Resync all room codes to room locks","description":"Queues every valid stored room code to every code-capable lock assigned to each room.","operationId":"resyncAllRoomCodes","responses":{"200":{"description":"Resync commands queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomCodeResyncResponse"}}}}},"security":[{"bearerAuth":[]}]}},"/api/bookings/bookings":{"get":{"tags":["Bookings"],"summary":"List bookings","description":"Returns all bookings with optional filters for check-in/out status and date range.","operationId":"listBookings","parameters":[{"name":"checkedIn","in":"query","description":"Filter by checked-in state","required":false,"schema":{"type":"boolean"}},{"name":"checkedOut","in":"query","description":"Filter by checked-out state","required":false,"schema":{"type":"boolean"}},{"name":"startDate","in":"query","description":"Filter start date (inclusive, yyyy-MM-dd)","required":false,"schema":{"type":"string","format":"date"}},{"name":"endDate","in":"query","description":"Filter end date (inclusive, yyyy-MM-dd)","required":false,"schema":{"type":"string","format":"date"}},{"name":"startingToday","in":"query","description":"Only bookings starting today","required":false,"schema":{"type":"boolean","default":false}},{"name":"activeToday","in":"query","description":"Only bookings that have started and not yet ended","required":false,"schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"List of bookings","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Booking"}}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Bookings"],"summary":"Create booking","description":"Creates a new booking.","operationId":"createBooking","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}},"required":true},"responses":{"200":{"description":"Created booking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"Invalid payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"409":{"description":"Booking already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}}},"security":[{"bearerAuth":[]}]}},"/api/bookings/bookings/{bookingId}/send-message":{"post":{"tags":["Bookings"],"summary":"Send automated message to booking guest","description":"Triggers the configured automated message for a booking with optional contact overrides.","operationId":"sendBookingMessage","parameters":[{"name":"bookingId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingMessageRequest"}}}},"responses":{"202":{"description":"Message queued"},"400":{"description":"No applicable trigger or contact details"},"404":{"description":"Booking not found"}},"security":[{"bearerAuth":[]}]}},"/api/bookings/bookings/{bookingId}/message-preview":{"post":{"tags":["Bookings"],"summary":"Preview automated message for booking guest","description":"Returns the rendered automated message (email/SMS) for confirmation before sending.","operationId":"previewBookingMessage","parameters":[{"name":"bookingId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingMessageRequest"}}}},"responses":{"200":{"description":"Preview status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingMessagePreviewResponse"}}}},"404":{"description":"Booking not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingMessagePreviewResponse"}}}}},"security":[{"bearerAuth":[]}]}},"/api/bookings/bookings/{bookingId}/code":{"post":{"tags":["Bookings"],"summary":"Set booking room code","description":"Assigns a specific code to the booking. If the code already exists on the booking room it is reused; otherwise it is added to the room first.","operationId":"setCode","parameters":[{"name":"bookingId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingRoomCodeRequest"}}},"required":true},"responses":{"200":{"description":"Updated booking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"Booking cannot be assigned the room code","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"404":{"description":"Booking not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"409":{"description":"Code already belongs to another room or booking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}}},"security":[{"bearerAuth":[]}]}},"/api/bookings/bookings/{bookingId}/code/refresh":{"post":{"tags":["Bookings"],"summary":"Refresh booking room code","description":"Picks a room code for the booking and stores it on the booking.","operationId":"refreshCode","parameters":[{"name":"bookingId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Updated booking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"Booking cannot be assigned a room code","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"404":{"description":"Booking not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}}},"security":[{"bearerAuth":[]}]}},"/api/automation/messages/triggers":{"get":{"tags":["Automated Messages"],"summary":"List configured triggers","description":"Lists all configured automation triggers for the current tenant.","operationId":"listTriggers","responses":{"200":{"description":"List of triggers","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AutomationTrigger"}}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Automated Messages"],"summary":"Create trigger","description":"Create an automated message trigger from the supported trigger list.","operationId":"createTrigger","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationTriggerRequest"}}},"required":true},"responses":{"200":{"description":"Trigger created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationTrigger"}}}},"400":{"description":"Invalid trigger request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationTrigger"}}}}},"security":[{"bearerAuth":[]}]}},"/api/auth/session":{"get":{"tags":["auth-session-controller"],"operationId":"getSession","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"boolean"}}}}}}},"post":{"tags":["auth-session-controller"],"operationId":"createSession","parameters":[{"name":"Authorization","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}},"delete":{"tags":["auth-session-controller"],"operationId":"clearSession","responses":{"200":{"description":"OK"}}}},"/api/api-users":{"get":{"tags":["API users"],"operationId":"list_2","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ApiUser"}}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["API users"],"operationId":"create_3","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiUser"}}},"required":true},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiUser"}}}}},"security":[{"bearerAuth":[]}]}},"/api/api-users/{id}/rotate":{"post":{"tags":["API users"],"operationId":"rotate","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiUser"}}}}},"security":[{"bearerAuth":[]}]}},"/api/ai/threads/message":{"post":{"tags":["AI Chat Threads"],"summary":"Send message and get AI reply","description":"Stores the user message in the thread and returns the generated AI reply.","operationId":"sendMessage","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIChatReplyRequest"}}},"required":true},"responses":{"200":{"description":"Reply returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIChatReplyResponse"}}}},"400":{"description":"Missing session_id or text","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIChatReplyResponse"}}}},"404":{"description":"Thread not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIChatReplyResponse"}}}}},"security":[{"bearerAuth":[]}]}},"/api/users/testpushnotification/{fcmToken}":{"get":{"tags":["Users"],"summary":"Register a user for push notifications","operationId":"testPushNotification","parameters":[{"name":"fcmToken","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"bearerAuth":[]}]}},"/api/users/pushnotification/{tenantId}":{"get":{"tags":["Users"],"summary":"Register a user for push notifications","operationId":"getAllNotifications","parameters":[{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DevicePushRegistration"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/temperature-control/stats":{"get":{"tags":["Temperature Control"],"summary":"Get latest aggregate temperature stats","description":"Returns the most recent 5-minute aggregate snapshot for the current tenant.","operationId":"getLatestStats","responses":{"200":{"description":"Latest stats returned successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemperatureStatsResponse"}}}},"400":{"description":"Missing tenant context","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemperatureStatsResponse"}}}}},"security":[{"bearerAuth":[]}]}},"/api/temperature-control/history/{sensorId}":{"get":{"tags":["Temperature Control"],"summary":"Get temperature history for a sensor","description":"Returns the collected 5-minute temperature readings for a specific sensor.","operationId":"getSensorHistory","parameters":[{"name":"sensorId","in":"path","description":"Sensor id","required":true,"schema":{"type":"string"},"example":"A1B2C3D4E5F60708"}],"responses":{"200":{"description":"Sensor temperature history returned successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TemperatureSensorHistory"}}}}},"400":{"description":"Missing tenant context or sensor id","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TemperatureSensorHistory"}}}}},"404":{"description":"Sensor not found","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TemperatureSensorHistory"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/smartlocks/{id}/packages/pulse-open":{"get":{"tags":["Smart Locks"],"summary":"Download pulse-open package","description":"Returns the Bluetooth data package for the mobile app to forward directly to the smart lock.","operationId":"pulseOpenPackage","parameters":[{"name":"id","in":"path","description":"Smart lock sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pulse-open package returned","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SmartLockOperationPackage"}}}},"400":{"description":"Invalid sensor","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SmartLockOperationPackage"}}}},"404":{"description":"Sensor not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SmartLockOperationPackage"}}}}},"security":[{"bearerAuth":[]}]}},"/api/smartlocks/{id}/packages/open":{"get":{"tags":["Smart Locks"],"summary":"Download open package","description":"Returns the Bluetooth data package for the mobile app to forward directly to the smart lock.","operationId":"openPackage","parameters":[{"name":"id","in":"path","description":"Smart lock sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Open package returned","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SmartLockOperationPackage"}}}},"400":{"description":"Invalid sensor","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SmartLockOperationPackage"}}}},"404":{"description":"Sensor not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SmartLockOperationPackage"}}}}},"security":[{"bearerAuth":[]}]}},"/api/smartlocks/{id}/packages/lock":{"get":{"tags":["Smart Locks"],"summary":"Download lock package","description":"Returns the Bluetooth data package for the mobile app to forward directly to the smart lock.","operationId":"lockPackage","parameters":[{"name":"id","in":"path","description":"Smart lock sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Lock package returned","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SmartLockOperationPackage"}}}},"400":{"description":"Invalid sensor","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SmartLockOperationPackage"}}}},"404":{"description":"Sensor not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SmartLockOperationPackage"}}}}},"security":[{"bearerAuth":[]}]}},"/api/smartlocks/{id}/codes/slots":{"get":{"tags":["Smart Locks"],"summary":"List smart lock code slots","description":"Returns the current code slots and their states for a smart lock.","operationId":"listSlots","parameters":[{"name":"id","in":"path","description":"Smart lock sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Slot list returned","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SmartLockCodeSlot"}}}}},"400":{"description":"Invalid sensor","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SmartLockCodeSlot"}}}}},"404":{"description":"Sensor not found","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SmartLockCodeSlot"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/sensor":{"get":{"tags":["Sensors"],"summary":"Get all sensors","description":"Retrieves a complete list of all sensors available under the authenticated user's tenant.","operationId":"getAllSensors","responses":{"200":{"description":"List of sensors returned successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sensor"}}}}},"security":[{"bearerAuth":[]}]}},"/api/sensor/{id}/taken":{"get":{"tags":["Sensors"],"summary":"Check if a sensor is claimed","description":"Returns true if the sensor UUID is already claimed by any tenant.","operationId":"isSensorTaken","parameters":[{"name":"id","in":"path","description":"Sensor UUID (payload.getUUIDString())","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Claim status returned successfully","content":{"*/*":{"schema":{"type":"boolean"}}}},"400":{"description":"Missing sensor id","content":{"*/*":{"schema":{"type":"boolean"}}}}},"security":[{"bearerAuth":[]}]}},"/api/sensor/{id}/messages":{"get":{"tags":["Sensors"],"summary":"Get all communication messages for a sensor","description":"Retrieves a chronological log of all messages exchanged with a particular sensor.\n\nUseful for debugging, diagnostics, and analytics of node communication.\n","operationId":"getAllMessages","parameters":[{"name":"id","in":"path","description":"Sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Message list returned successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/sensor/message/{id}/{gwid}":{"get":{"tags":["Sensors"],"summary":"Resend a message to a gateway","description":"Re-dispatches a specific message from the backend to a given gateway.\n\nTypically used when message delivery failed or needs re-acknowledgment.\n","operationId":"resendMessage","parameters":[{"name":"id","in":"path","description":"Message ID (hex string)","required":true,"schema":{"type":"string"}},{"name":"gwid","in":"path","description":"Gateway ID that should receive the message","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Message resend triggered successfully"},"404":{"description":"Message or gateway not found"}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Sensors"],"summary":"Delete a message","description":"Deletes a previously queued or sent message from the backend.","operationId":"deleteMessage","parameters":[{"name":"id","in":"path","description":"Message ID (hex string)","required":true,"schema":{"type":"string"}},{"name":"gwid","in":"path","description":"Gateway ID (unused for deletion but logged)","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Message deleted successfully"},"404":{"description":"Message not found"}},"security":[{"bearerAuth":[]}]}},"/api/sensor/groups/by-sensor/{sensorId}":{"get":{"tags":["Sensors"],"summary":"Get sensor groups by sensor id","description":"Returns all groups that include the provided sensor id.","operationId":"getGroupsBySensorId","parameters":[{"name":"sensorId","in":"path","description":"Sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Groups returned successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SensorGroup"}}}}},"security":[{"bearerAuth":[]}]}},"/api/sensor/groups/by-gateway/{gatewayId}":{"get":{"tags":["Sensors"],"summary":"Get sensor groups by gateway id","description":"Returns all groups that include the provided gateway id.","operationId":"getGroupsByGatewayId","parameters":[{"name":"gatewayId","in":"path","description":"Gateway ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Groups returned successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SensorGroup"}}}}},"security":[{"bearerAuth":[]}]}},"/api/node-updates/firmwares":{"get":{"tags":["Node Updates"],"summary":"List node firmware images","operationId":"listFirmwares","responses":{"200":{"description":"Node firmware list returned","content":{"*/*":{"schema":{"$ref":"#/components/schemas/NodeFirmwareInfo"}}}}},"security":[{"bearerAuth":[]}]}},"/api/messages":{"get":{"tags":["tenant-message-controller"],"operationId":"getMessages","parameters":[{"name":"since","in":"query","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageHistoryResponse"}}}}}}},"/api/guest-portal/{tenantId}/bookings/{bookingId}/room":{"get":{"tags":["Guest Portal"],"summary":"Get room name for a booking","operationId":"getRoomFromTenantPath","parameters":[{"name":"tenantId","in":"path","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"string"}}}}}}},"/api/guest-portal/{tenantId}/bookings/{bookingId}/roomName":{"get":{"tags":["Guest Portal"],"summary":"Get room name for a booking","operationId":"getRoomNameFromTenantPath","parameters":[{"name":"tenantId","in":"path","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomNameResponse"}}}}}}},"/api/guest-portal/frames/{id}":{"get":{"tags":["Guest Portal"],"summary":"Get message frame by id","operationId":"getFrame","parameters":[{"name":"tenantId","in":"query","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"query","description":"Booking ID","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","description":"Message frame ID (hex)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}}}},"/api/guest-portal/bookings/{bookingId}/validity":{"get":{"tags":["Guest Portal"],"summary":"Get booking validity status","operationId":"getBookingValidity","parameters":[{"name":"tenantId","in":"query","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingValidityResponse"}}}}}}},"/api/guest-portal/bookings/{bookingId}/sensors":{"get":{"tags":["Guest Portal"],"summary":"List sensors for a booking","operationId":"getSensors","parameters":[{"name":"tenantId","in":"query","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Sensor"}}}}}}}},"/api/guest-portal/bookings/{bookingId}/roomcode":{"get":{"tags":["Guest Portal"],"summary":"Get room code for a booking","operationId":"getRoomCode","parameters":[{"name":"tenantId","in":"query","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"string"}}}}}}},"/api/guest-portal/bookings/{bookingId}/room":{"get":{"tags":["Guest Portal"],"summary":"Get room name for a booking","operationId":"getRoom","parameters":[{"name":"tenantId","in":"query","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"string"}}}}}}},"/api/guest-portal/bookings/{bookingId}/roomName":{"get":{"tags":["Guest Portal"],"summary":"Get room name for a booking","operationId":"getRoomName","parameters":[{"name":"tenantId","in":"query","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"string"}}}}}}},"/api/guest-portal/bookings/{bookingId}/offers":{"get":{"tags":["Guest Portal"],"summary":"List active guest portal offers for a booking","operationId":"getGuestPortalOffers","parameters":[{"name":"tenantId","in":"query","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}},{"name":"language","in":"query","description":"Language code","required":false,"schema":{"type":"string"},"example":"en"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GuestPortalOffer"}}}}}}}},"/api/guest-portal/bookings/{bookingId}/messages":{"get":{"tags":["Guest Portal"],"summary":"Get guest portal message by booking and language","operationId":"getGuestPortalMessage","parameters":[{"name":"tenantId","in":"query","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}},{"name":"language","in":"query","description":"Language code","required":true,"schema":{"type":"string"},"example":"en"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestPortalMessage"}}}}}}},"/api/guest-portal/bookings/{bookingId}/languages":{"get":{"tags":["Guest Portal"],"summary":"List activated guest portal languages for a booking","operationId":"getActivatedLanguagesForBooking","parameters":[{"name":"tenantId","in":"query","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GuestPortalLanguageOption"}}}}}}}},"/api/guest-portal/bookings/{bookingId}/directions":{"get":{"tags":["Guest Portal"],"summary":"Get step-by-step directions for a booking's room","operationId":"getDirections","parameters":[{"name":"tenantId","in":"query","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}},{"name":"language","in":"query","description":"Language code","required":false,"schema":{"type":"string","default":"en"},"example":"en"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestPortalDirectionsResponse"}}}}}}},"/api/guest-portal/bookings/{bookingId}/direction-images/{imageId}":{"get":{"tags":["Guest Portal"],"summary":"Get an image referenced by a booking's direction route","operationId":"getDirectionImage","parameters":[{"name":"tenantId","in":"query","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}},{"name":"imageId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string","format":"byte"}}}}}}}},"/api/guest-portal/bookings/{bookingId}/ai/activated":{"get":{"tags":["Guest Portal"],"summary":"Check whether AI module is activated for a booking tenant","operationId":"isAIModuleActivated","parameters":[{"name":"tenantId","in":"query","description":"Tenant ID","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIModuleActivatedResponse"}}}}}}},"/api/guest-portal-admin/languages":{"get":{"tags":["Guest Portal Admin"],"summary":"List all selectable languages for guest portal","operationId":"getAvailableLanguages","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GuestPortalLanguageOption"}}}}}}}},"/api/guest-portal-admin/direction-variables":{"get":{"tags":["Guest Portal Admin"],"summary":"List supported guest direction template variables","operationId":"listDirectionVariables","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}}}}},"/api/guest-portal-admin/direction-images/{imageId}":{"get":{"tags":["Guest Portal Admin"],"summary":"Get guest direction image metadata","operationId":"getDirectionImage_1","parameters":[{"name":"imageId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestPortalDirectionImage"}}}}}},"delete":{"tags":["Guest Portal Admin"],"summary":"Delete a guest direction image","operationId":"deleteDirectionImage","parameters":[{"name":"imageId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/gateways":{"get":{"tags":["Gateways"],"summary":"List all gateways","description":"Retrieve a list of all registered Solvotix gateways in the system.","operationId":"getAll","responses":{"200":{"description":"List of gateways returned successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Gateway"}}}}},"security":[{"bearerAuth":[]}]}},"/api/gateways/{id}/sensors":{"get":{"tags":["Gateways"],"summary":"List sensors connected to a gateway","description":"Returns all sensors currently associated with the specified gateway.","operationId":"getSensors_1","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Sensor"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/gateways/{id}/refreshqueue":{"get":{"tags":["Gateways"],"summary":"Request refreshed node list","description":"Instructs a gateway to perform a fresh scan of all connected Solvotix nodes.","operationId":"refreshNodeList","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"bearerAuth":[]}]}},"/api/gateways/{id}/loadqueue":{"get":{"tags":["Gateways"],"summary":"Trigger queue reload","description":"Asks a gateway to refresh its message queue state and send updates back to the backend.","operationId":"loadqueue","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"bearerAuth":[]}]}},"/api/gateways/{id}/gwqueue":{"get":{"tags":["Gateways"],"summary":"Get gateway message queue","description":"Download the currently queued messages for a specific gateway.","operationId":"getQueue","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MessageFrame"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/gateways/{id}/action/{action}":{"get":{"tags":["Gateways"],"summary":"Execute a remote gateway action","description":"Executes a remote command or predefined action on the specified gateway.\nThis may include rebooting, rescanning, or other maintenance operations.\n","operationId":"doAction","parameters":[{"name":"id","in":"path","description":"Gateway ID","required":true,"schema":{"type":"string"}},{"name":"action","in":"path","description":"Action name to perform (e.g., 'reboot', 'scan')","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Action executed successfully","content":{"application/json":{"schema":{"type":"boolean"}}}},"404":{"description":"Gateway not found or action failed","content":{"application/json":{"schema":{"type":"boolean"}}}}},"security":[{"bearerAuth":[]}]}},"/api/gateways/{gatewayId}/nrffirmware":{"get":{"tags":["Gateways"],"summary":"Get NRF firmware for gateway","description":"Returns the latest NRF (Bluetooth controller) firmware image for a given gateway.\nIf the current version matches the latest, a 404 response is returned.\n","operationId":"getNrfFirmware","parameters":[{"name":"gatewayId","in":"path","description":"Gateway ID requesting NRF firmware","required":true,"schema":{"type":"string"}},{"name":"current","in":"query","description":"Current NRF firmware version","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Firmware binary returned successfully","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"No update available or firmware not found","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}}},"security":[{"bearerAuth":[]}]}},"/api/gateways/{gatewayId}/nodefirmware":{"get":{"tags":["Gateways"],"summary":"Get Node firmware via gateway","description":"Retrieves the latest firmware image for a connected **Solvotix Node**.\nThis is typically relayed through a gateway during OTA updates.\n","operationId":"nodeFirmware","parameters":[{"name":"gatewayId","in":"path","description":"Gateway ID managing the node","required":true,"schema":{"type":"string"}},{"name":"current","in":"query","description":"Current node firmware version","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Node firmware binary returned successfully","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"No update available or firmware not found","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}}},"security":[{"bearerAuth":[]}]}},"/api/gateways/{gatewayId}/metering":{"get":{"tags":["Gateways"],"summary":"Get gateway metering data","description":"Returns a bounded, sequence-ordered page of raw metering records for a gateway.","operationId":"getMeteringData","parameters":[{"name":"gatewayId","in":"path","description":"Gateway ID","required":true,"schema":{"type":"string"}},{"name":"subAction","in":"query","description":"Optional metering subaction: 12, 13, 14, or 15","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","format":"int32","default":0}},{"name":"size","in":"query","required":false,"schema":{"type":"integer","format":"int32","default":100}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageGatewayMeteringRecord"}}}}},"security":[{"bearerAuth":[]}]}},"/api/gateways/{gatewayId}/metering/latest":{"get":{"tags":["Gateways"],"summary":"Get latest gateway metering record","description":"Returns the metering record with the highest sequence for a gateway.","operationId":"getLatestMeteringData","parameters":[{"name":"gatewayId","in":"path","description":"Gateway ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayMeteringRecord"}}}}},"security":[{"bearerAuth":[]}]}},"/api/gateways/{gatewayId}/firmware":{"get":{"tags":["Gateways"],"summary":"Get firmware for gateway or sensor OTA","description":"Retrieves the latest firmware binary for a Solvotix gateway or thermostat sensor OTA request.\nOnly returns data if a newer version is available than the one currently installed.\n","operationId":"getFirmware","parameters":[{"name":"gatewayId","in":"path","description":"Gateway ID requesting firmware","required":true,"schema":{"type":"string"}},{"name":"current","in":"query","description":"Currently installed firmware version","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Firmware binary returned successfully","content":{"application/octet-stream":{}}},"404":{"description":"No update available or firmware not found","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}}},"security":[{"bearerAuth":[]}]}},"/api/gateways/queue/device/{deviceId}":{"get":{"tags":["Gateways"],"summary":"Get outbound queue for a device","description":"Returns all pending outbound queue messages targeted at a specific device (node) ID.","operationId":"getQueueForDevice","parameters":[{"name":"deviceId","in":"path","description":"Device (node) ID to filter the queue by","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Queue messages returned successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/gateways/getqueue":{"get":{"tags":["Gateways"],"summary":"Get outbound queue for current tenant","description":"Returns all pending outbound queue messages scoped to the tenant resolved from the JWT.","operationId":"getQueueForCurrentTenant","responses":{"200":{"description":"Queue messages returned successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayQueueMessage"}}}}},"security":[{"bearerAuth":[]}]}},"/api/events/sensor/":{"get":{"tags":["Events"],"summary":"Get events for a sensor in a time period","description":"Retrieves Solvotix events for the specified sensor UUID within the given time period.\nThe range is inclusive and both `from` and `to` must be ISO-8601 timestamps.\n","operationId":"getEventsForSensor","parameters":[{"name":"from","in":"query","description":"Inclusive range start in ISO-8601 format","required":true,"schema":{"type":"string"},"example":"2026-04-01T00:00:00Z"},{"name":"to","in":"query","description":"Inclusive range end in ISO-8601 format","required":true,"schema":{"type":"string"},"example":"2026-04-15T23:59:59Z"}],"responses":{"200":{"description":"Sensor events retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SolvotixEvent"}}}},"400":{"description":"Invalid sensor UUID or time range"}},"security":[{"bearerAuth":[]}]}},"/api/events/sensor/{sensorUuid}":{"get":{"tags":["Events"],"summary":"Get events for a sensor in a time period","description":"Retrieves Solvotix events for the specified sensor UUID within the given time period.\nThe range is inclusive and both `from` and `to` must be ISO-8601 timestamps.\n","operationId":"getEventsForSensor_1","parameters":[{"name":"sensorUuid","in":"path","description":"Sensor UUID to filter by","required":true,"schema":{"type":"string"},"example":"A1B2C3D4E5F60708"},{"name":"from","in":"query","description":"Inclusive range start in ISO-8601 format","required":true,"schema":{"type":"string"},"example":"2026-04-01T00:00:00Z"},{"name":"to","in":"query","description":"Inclusive range end in ISO-8601 format","required":true,"schema":{"type":"string"},"example":"2026-04-15T23:59:59Z"}],"responses":{"200":{"description":"Sensor events retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SolvotixEvent"}}}},"400":{"description":"Invalid sensor UUID or time range"}},"security":[{"bearerAuth":[]}]}},"/api/events/sensor":{"get":{"tags":["Events"],"summary":"Get events for a sensor in a time period","description":"Retrieves Solvotix events for the specified sensor UUID within the given time period.\nThe range is inclusive and both `from` and `to` must be ISO-8601 timestamps.\n","operationId":"getEventsForSensor_2","parameters":[{"name":"from","in":"query","description":"Inclusive range start in ISO-8601 format","required":true,"schema":{"type":"string"},"example":"2026-04-01T00:00:00Z"},{"name":"to","in":"query","description":"Inclusive range end in ISO-8601 format","required":true,"schema":{"type":"string"},"example":"2026-04-15T23:59:59Z"}],"responses":{"200":{"description":"Sensor events retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SolvotixEvent"}}}},"400":{"description":"Invalid sensor UUID or time range"}},"security":[{"bearerAuth":[]}]}},"/api/events/byRoom/{roomId}":{"get":{"tags":["Events"],"summary":"Get room events in a time period","description":"Retrieves Solvotix events connected to a room within the given time period.\nThe result includes booking lifecycle events, events connected to matching bookings,\nlock code add/remove events for room sensors, and lock-user entry events for room sensors.\nBoth `startdate` and `enddate` must be ISO-8601 timestamps.\n","operationId":"byRoom","parameters":[{"name":"roomId","in":"path","description":"Room id to filter by","required":true,"schema":{"type":"string"},"example":"room-101"},{"name":"startdate","in":"query","description":"Inclusive range start in ISO-8601 format","required":true,"schema":{"type":"string"},"example":"2026-04-01T00:00:00Z"},{"name":"enddate","in":"query","description":"Inclusive range end in ISO-8601 format","required":true,"schema":{"type":"string"},"example":"2026-04-15T23:59:59Z"}],"responses":{"200":{"description":"Room events retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SolvotixEvent"}}}},"400":{"description":"Invalid room id or time range"},"404":{"description":"Room not found"}},"security":[{"bearerAuth":[]}]}},"/api/cleaning/rooms":{"get":{"tags":["Cleaning"],"summary":"List rooms with cleaning status","operationId":"listRooms","responses":{"200":{"description":"List of tracked rooms","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CleaningRoom"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/bookings/rooms/{roomId}/codes/random/pick":{"get":{"tags":["Bookings"],"summary":"Pick random room code","description":"Returns a random room code and ensures it is present on all room locks.","operationId":"pickRandomRoomCode","parameters":[{"name":"roomId","in":"path","required":true,"schema":{"type":"string"}},{"name":"bookingId","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Random code returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomCodeResponse"}}}},"404":{"description":"Room not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomCodeResponse"}}}}},"security":[{"bearerAuth":[]}]}},"/api/bookings/categories":{"get":{"tags":["Bookings"],"summary":"List categories","operationId":"listCategories","responses":{"200":{"description":"List of categories","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Categories"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/automation":{"get":{"tags":["Automation"],"summary":"List automation scenarios","description":"Returns the built-in automation sequences available for building operations.","operationId":"listAutomationScenarios","responses":{"200":{"description":"List of automation scenarios","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AutomationCapability"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/automation/messages":{"get":{"tags":["Automated Messages"],"summary":"List automated messages","description":"Provides the default messages emitted when automation triggers fire.","operationId":"listAutomatedMessages","responses":{"200":{"description":"List of automated messages","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AutomationMessage"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/automation/messages/triggers/config":{"get":{"tags":["Automated Messages"],"summary":"List supported trigger types","description":"Returns the available trigger configuration options that can be created.","operationId":"listTriggerDefinitions","responses":{"200":{"description":"List of available triggers","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AutomationTriggerDefinition"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/ai/threads":{"get":{"tags":["AI Chat Threads"],"summary":"List all AI chat threads","description":"Returns a summary view of all AI chat threads for the current tenant.","operationId":"listThreads","responses":{"200":{"description":"Thread summaries returned","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AIChatThreadSummary"}}}}}},"security":[{"bearerAuth":[]}]}},"/api/ai/threads/{sessionId}":{"get":{"tags":["AI Chat Threads"],"summary":"Get full AI chat thread","description":"Returns the entire chat thread, including all messages, for a summary item selected by session id.","operationId":"getThread","parameters":[{"name":"sessionId","in":"path","description":"Session id of the thread to fetch.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Thread returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIChatThread"}}}},"404":{"description":"Thread not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIChatThread"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["AI Chat Threads"],"summary":"Delete AI chat thread","description":"Deletes a thread by session id.","operationId":"deleteThread","parameters":[{"name":"sessionId","in":"path","description":"Session id of the thread to delete.","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Thread deleted"},"404":{"description":"Thread not found"}},"security":[{"bearerAuth":[]}]}},"/api/users/{id}/account":{"delete":{"tags":["Users"],"summary":"Delete user account and all related data","description":"Permanently deletes a user account for the current tenant and removes\nall related records, including:\n\n- Tenant membership (security)\n- Push notification registrations\n- Email notification registrations\n\nIf the user no longer belongs to any tenants after this operation,\ntheir Firebase Authentication account may also be deleted on the backend.\n","operationId":"deleteAccountAndRelatedData","parameters":[{"name":"id","in":"path","description":"User ID whose account should be deleted","required":true,"schema":{"type":"string"},"example":"u_001"}],"responses":{"200":{"description":"Account and related data deleted successfully"},"401":{"description":"Unauthorized"},"404":{"description":"User not found or already deleted"}},"security":[{"bearerAuth":[]}]}},"/api/users/pushnotification/{id}":{"delete":{"tags":["Users"],"summary":"Delete a specific push notification registration by ID","operationId":"deleteNotificationById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"bearerAuth":[]}]}},"/api/users/emailnotification/{id}":{"delete":{"tags":["Users"],"summary":"Delete a specific email notification registration by ID","description":"Removes the email notification preference with the given identifier.","operationId":"deleteEmailNotificationById","parameters":[{"name":"id","in":"path","description":"Email registration ID","required":true,"schema":{"type":"string"},"example":"eml_001"}],"responses":{"200":{"description":"Deleted successfully"},"401":{"description":"Unauthorized"},"404":{"description":"Registration not found"}},"security":[{"bearerAuth":[]}]}},"/api/smartlocks/{id}/codes":{"delete":{"tags":["Smart Locks"],"summary":"Delete all access codes","description":"Deletes all access codes on the smart lock.","operationId":"deleteAllCodes","parameters":[{"name":"id","in":"path","description":"Smart lock sensor ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Delete codes command queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"400":{"description":"Invalid sensor or payload","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}},"404":{"description":"Sensor not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageFrame"}}}}},"security":[{"bearerAuth":[]}]}},"/api/node-updates/sensors/{sensorId}/queue":{"delete":{"tags":["Node Updates"],"summary":"Remove all queued messages for a sensor","description":"Deletes all queued messages (OTA and regular) for the given sensor. Also resets any active update state.","operationId":"removeAllQueuedMessages","parameters":[{"name":"sensorId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Messages removed, returns count","content":{"*/*":{"schema":{"type":"integer","format":"int32"}}}},"400":{"description":"Invalid sensor ID","content":{"*/*":{"schema":{"type":"integer","format":"int32"}}}}},"security":[{"bearerAuth":[]}]}},"/api/gateways/{id}/message/{message_id}":{"delete":{"tags":["Gateways"],"summary":"Delete a message on a gateway","description":"Removes a specific message from the gateway's queue.","operationId":"deleteMessageOnGateway","parameters":[{"name":"id","in":"path","description":"Gateway ID","required":true,"schema":{"type":"string"}},{"name":"message_id","in":"path","description":"Message ID to delete","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Message deleted successfully"},"404":{"description":"Message not found or already deleted"}},"security":[{"bearerAuth":[]}]}},"/api/gateways/queue/message/{messageId}":{"delete":{"tags":["Gateways"],"summary":"Delete a message from the outbound queue","description":"Removes a specific message from the outbound message queue by its message ID.","operationId":"removeMessageFromQueue","parameters":[{"name":"messageId","in":"path","description":"Message ID to remove from the queue","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Message removed from queue successfully"},"404":{"description":"Message not found in queue"}},"security":[{"bearerAuth":[]}]}},"/api/bookings/rooms/{roomId}/codes/{code}":{"delete":{"tags":["Bookings"],"summary":"Remove room code","description":"Removes a specific 4-7 digit code from the room.","operationId":"removeRoomCode","parameters":[{"name":"roomId","in":"path","required":true,"schema":{"type":"string"}},{"name":"code","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Updated room","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rooms"}}}},"404":{"description":"Room not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rooms"}}}}},"security":[{"bearerAuth":[]}]}},"/api/api-users/{id}":{"delete":{"tags":["API users"],"operationId":"revoke","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No Content"}},"security":[{"bearerAuth":[]}]}}},"components":{"schemas":{"User":{"type":"object","properties":{"uid":{"type":"string","description":"The id of the user","example":"UUID"},"email":{"type":"string","description":"The email of the user","example":"ola@norman.no"},"name":{"type":"string","description":"The full name of the user","example":"Ola Norman"},"language":{"type":"string","description":"Preferred UI language for the user","example":"en"},"last_sign_in":{"type":"string","description":"The date when last signed in","format":"date-time"},"roles":{"type":"array","description":"Roles assigned to the user. If empty, the user has unrestricted access.","example":["User"],"items":{"type":"string","description":"Roles assigned to the user. If empty, the user has unrestricted access.","example":"[\"User\"]"}}},"description":"Information about the user"},"DevicePushRegistration":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"fcmToken":{"type":"string"},"platform":{"type":"string"},"deviceModel":{"type":"string"},"tenantId":{"type":"string"},"deviceId":{"type":"string"},"enabled":{"type":"boolean"}}},"UserPushNotificationSettings":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"tenantId":{"type":"string","description":"Tenant id owning these settings","example":"tenant123"},"gatewayOnline":{"type":"boolean","description":"Notify when a gateway comes online","example":true},"gatewayOffline":{"type":"boolean","description":"Notify when a gateway goes offline","example":true},"sensorOffline":{"type":"boolean","description":"Notify when a sensor goes offline","example":true},"sensorOnline":{"type":"boolean","description":"Notify when a sensor comes online","example":true},"alarmActive":{"type":"boolean","description":"Notify when an alarm is active","example":true},"alarmCleared":{"type":"boolean","description":"Notify when an alarm is cleared","example":true},"sensorBatteryLow":{"type":"boolean","description":"Notify when sensor battery is low","example":true},"smartLockCodeUploadDelayed":{"type":"boolean","description":"Notify when a smart lock code upload is delayed","example":true},"smartLockValidCodeRejected":{"type":"boolean","description":"Notify when a smart lock rejects a code marked valid and uploaded in the backend","example":true}},"description":"Per-user push notification settings for the current tenant"},"EmailNotificationRegistration":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"enabled":{"type":"boolean","description":"Whether event emails are enabled for this user","example":true}},"description":"Per-user email notification preference (tenant is implied from headers)"},"Tenant":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"name":{"type":"string","description":"Name of the tenant","example":"Acme Corporation"},"description":{"type":"string","description":"A description for the tenant","example":"The story of your life"},"timezone":{"type":"string","description":"IANA time zone identifier used for scheduling and display","example":"Europe/Oslo"},"smtpHost":{"type":"string","description":"Optional SMTP host to override the default mail server","example":"smtp.example.com"},"smtpPort":{"type":"integer","description":"SMTP port for the tenant-specific mail server","format":"int32","example":587},"smtpUsername":{"type":"string","description":"Username for authenticating with the tenant-specific mail server"},"smtpPassword":{"type":"string","description":"Password for authenticating with the tenant-specific mail server"},"smtpFrom":{"type":"string","description":"From address to use when sending emails for this tenant","example":"Alerts <alerts@example.com>"},"smtpFromName":{"type":"string","description":"Display name to use in the From header when sending emails","example":"Solvotix Support"},"imapHost":{"type":"string","description":"IMAP host used for reading inbound emails","example":"imap.zoho.eu"},"imapPort":{"type":"integer","description":"IMAP port used for inbound mailbox access","format":"int32","example":993},"imapSsl":{"type":"boolean","description":"Whether SSL is required for IMAP connections","example":true},"instructionsForParsing":{"type":"string","description":"Custom instructions used when parsing lead-related input/content"},"instructionsForMailGeneration":{"type":"string","description":"Custom instructions used when generating lead-related emails"},"smsSender":{"type":"string","description":"Sender name/number to use when sending SMS for this tenant","example":"MyBuilding"}},"description":"Tenant object identifying which tenant to delete"},"SmartLockConfigurationRequest":{"type":"object","properties":{"soundEnabled":{"type":"boolean","description":"Enable or disable keypad sounds.","example":true},"lightEnabled":{"type":"boolean","description":"Enable or disable keypad light.","example":false},"openSeconds":{"type":"integer","description":"Seconds to keep the lock open.","format":"int32","example":6}},"description":"Smart lock configuration payload."},"Data":{"type":"object","properties":{"raw":{"type":"array","items":{"type":"string","format":"byte"}}}},"GatewaysDelivered":{"type":"object","properties":{"date":{"type":"string","format":"date-time"},"gw_id":{"type":"string"},"isAdding":{"type":"boolean"}}},"MessageFrame":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"msg":{"$ref":"#/components/schemas/server_message_t"},"uuid":{"type":"string"},"bookingId":{"type":"string"},"gatewayDelivery":{"type":"array","items":{"$ref":"#/components/schemas/GatewaysDelivered"}},"deliveredTime":{"type":"string","format":"date-time"}}},"NodeCoreMessage":{"type":"object","properties":{"id":{"type":"array","items":{"type":"string","format":"byte"}},"timestamp":{"type":"integer","format":"int32"},"action":{"type":"string","format":"byte"},"sub_action":{"type":"string","format":"byte"},"data":{"$ref":"#/components/schemas/Data"},"checksum":{"type":"string","format":"byte"},"idHex":{"type":"string"}}},"adv_payload_t":{"type":"object","properties":{"status":{"type":"string","format":"byte"},"node_type":{"type":"string","format":"byte"},"checksum":{"type":"string","format":"byte"},"battery_level":{"type":"string","format":"byte"},"timestamp":{"type":"integer","format":"int32"},"uuidstring":{"type":"string"},"uuid":{"type":"string"},"sensor_data":{"type":"string"},"fw_version":{"type":"string"},"node_settings":{"type":"string"}}},"server_message_t":{"type":"object","properties":{"magic":{"type":"string","format":"byte"},"uuid":{"type":"string"},"gw_id":{"type":"string"},"message":{"$ref":"#/components/schemas/NodeCoreMessage"},"adv_payload":{"$ref":"#/components/schemas/adv_payload_t"},"status":{"type":"string","format":"byte"},"delivered":{"type":"boolean"},"nrf_version":{"type":"array","items":{"type":"string","format":"byte"}},"rssi":{"type":"string","format":"byte"},"gwMessage":{"type":"boolean"},"nrfVersion":{"type":"integer","format":"int32"},"fireAndForget":{"type":"boolean"},"alarm":{"type":"boolean"}}},"SmartLockGlobalConfiguration":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"tenantId":{"type":"string"},"masterCode":{"type":"string"},"updatedAt":{"type":"string","format":"date-time"}}},"Sensor":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"name":{"type":"string"},"type":{"type":"string"},"location":{"type":"string"},"fw_version":{"type":"integer","format":"int32"},"unit":{"type":"string"},"active":{"type":"boolean"},"lastHeardFrom":{"type":"string","format":"date-time"},"isNew":{"type":"boolean"},"adv_data":{"$ref":"#/components/schemas/adv_payload_t"},"isUpdating":{"type":"boolean"},"progress":{"type":"integer","format":"int32"},"updateByGwId":{"type":"string"},"numberOfMessagesToDeliver":{"type":"integer","format":"int32"},"rssi":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}},"batteryHistory":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}},"seenByGateways":{"uniqueItems":true,"type":"array","items":{"type":"string"}},"configuration":{"type":"object","additionalProperties":{"type":"object"}},"pairedDevices":{"type":"array","items":{"$ref":"#/components/schemas/SensorPairedDevice"}},"pushNotificationDelayMinutes":{"type":"integer","format":"int32"},"state":{"type":"string"},"uuid":{"type":"string"},"advertisementData":{"$ref":"#/components/schemas/adv_payload_t"},"codeLock":{"type":"boolean"},"wirelessCodePanel":{"type":"boolean"},"gatewayWithBestRSSI":{"type":"string"},"temperatureSensor":{"type":"boolean"},"temperatureC":{"type":"number","format":"double"},"activeAlarm":{"type":"boolean"},"relay":{"type":"boolean"}}},"SensorPairedDevice":{"type":"object","properties":{"sensorId":{"type":"string"},"addedToDevice":{"type":"boolean"}}},"SensorPairingRequest":{"type":"object","properties":{"sensorIds":{"type":"array","items":{"type":"string"}}}},"TemperatureControlConfiguration":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"min_temp":{"type":"integer","description":"Minimum temperature threshold","format":"int32","example":18},"max_temp":{"type":"integer","description":"Maximum temperature threshold","format":"int32","example":26},"pms_room_in_use_temp":{"type":"integer","description":"Target temperature when PMS reports room in use","format":"int32","example":21},"pms_room_not_in_use_temp":{"type":"integer","description":"Target temperature when PMS reports room not in use","format":"int32","example":17}},"description":"Per-tenant temperature control configuration settings."},"SensorGroup":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"name":{"type":"string","description":"Optional display name for the group","example":"Floor 2 East Wing"},"sensorIds":{"type":"array","description":"Sensor ids included in the group","items":{"type":"string","description":"Sensor ids included in the group"}},"gatewayIds":{"type":"array","description":"Gateway ids included in the group","items":{"type":"string","description":"Gateway ids included in the group"}}},"description":"Group of sensors and gateways within a tenant."},"RelayConfigurationRequest":{"type":"object","properties":{"openMilliseconds":{"type":"integer","description":"Relay open duration in milliseconds (1-255).","format":"int32","example":150}},"description":"Relay configuration payload."},"ModuleToggle":{"type":"object","properties":{"key":{"type":"string","description":"Unique key for the module","example":"automation.temperature"},"title":{"type":"string","description":"Display title for the module","example":"Temperature Control"},"description":{"type":"string","description":"Short description of what the module provides"},"highlights":{"type":"array","description":"Bulleted highlights shown in the UI","items":{"type":"string","description":"Bulleted highlights shown in the UI"}},"enabled":{"type":"boolean","description":"Whether the module is active for this tenant"},"category":{"type":"string","description":"Category of the module","example":"automation"}},"description":"Tenant-scoped module toggle with presentation metadata."},"TenantModuleSettingsResponse":{"type":"object","properties":{"automation":{"type":"array","description":"Automation modules with activation state","items":{"$ref":"#/components/schemas/ModuleToggle"}},"integrations":{"type":"array","description":"Integration modules with activation state","items":{"$ref":"#/components/schemas/ModuleToggle"}}},"description":"Module activation state merged with presentation metadata."},"TenantModuleSettingsRequest":{"type":"object","properties":{"automation":{"type":"object","additionalProperties":{"type":"boolean","description":"Automation module states keyed by module id"},"description":"Automation module states keyed by module id"},"integrations":{"type":"object","additionalProperties":{"type":"boolean","description":"Integration module states keyed by module id"},"description":"Integration module states keyed by module id"}},"description":"Payload for updating tenant module activation toggles."},"LockUser":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"sensorIds":{"type":"array","description":"Smart lock sensor IDs assigned to this user.","items":{"type":"string","description":"Smart lock sensor IDs assigned to this user."}},"codes":{"type":"array","description":"Access codes assigned to this user.","items":{"type":"string","description":"Access codes assigned to this user."}},"codesNotUploadedToAllSensors":{"type":"integer","description":"Number of assigned lock/code pairs that have not been uploaded to the lock yet.","format":"int32"},"name":{"type":"string","description":"Display name for the lock user."},"description":{"type":"string","description":"Optional description for the lock user."},"email":{"type":"string","description":"Email address for the lock user."},"phone":{"type":"string","description":"Phone number for the lock user."}},"description":"User access profile for smart lock codes."},"GuestPortalMessage":{"type":"object","properties":{"roomId":{"type":"string","description":"Room identifier this message applies to.","example":"room-101"},"message":{"type":"string","description":"Message shown to the guest in the portal.","example":"Welcome! Your room is ready."},"language":{"type":"string","description":"Language code for the message.","example":"en"}},"description":"Custom guest portal message entry."},"GuestPortalOffer":{"type":"object","properties":{"id":{"type":"string","description":"Stable offer identifier"},"title":{"type":"string","description":"Offer title","example":"Late checkout"},"description":{"type":"string","description":"Offer body text","example":"Ask reception about late checkout availability."},"imageUrl":{"type":"string","description":"Optional image URL for the offer"},"encodedImage":{"type":"string","description":"Optional encoded image data for the offer, for example a Base64 data URL"},"linkUrl":{"type":"string","description":"Optional target URL opened when the guest selects the offer"},"buttonText":{"type":"string","description":"Optional button text","example":"View offer"},"language":{"type":"string","description":"Language code for this offer. Leave blank to show in all languages.","example":"en"},"active":{"type":"boolean","description":"Whether the offer should be shown","example":true},"priority":{"type":"integer","description":"Lower values are shown first","format":"int32","example":10},"validFrom":{"type":"string","description":"Offer is visible from this time, if set","format":"date-time"},"validTo":{"type":"string","description":"Offer is visible until this time, if set","format":"date-time"}},"description":"Offer or ad displayed in the guest portal."},"GuestPortalSettings":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"showRoom":{"type":"boolean","description":"Whether room name should be visible in guest portal.","example":true},"showCode":{"type":"boolean","description":"Whether access code should be visible in guest portal.","example":true},"limitToValidTime":{"type":"boolean","description":"Whether guest portal actions are limited to booking valid time window.","example":true},"showOpenButton":{"type":"boolean","description":"Whether the open button should be visible in guest portal.","example":true},"showCheckoutButton":{"type":"boolean","description":"Whether the checkout button should be visible in guest portal.","example":true},"activatedLanguages":{"type":"array","description":"Language codes activated for display in the guest portal.","example":["en","no"],"items":{"type":"string","description":"Language codes activated for display in the guest portal.","example":"[\"en\",\"no\"]"}},"messages":{"type":"array","description":"Custom guest portal messages configured for rooms and languages.","items":{"$ref":"#/components/schemas/GuestPortalMessage"}},"offers":{"type":"array","description":"Offers and ads displayed in the guest portal.","items":{"$ref":"#/components/schemas/GuestPortalOffer"}},"generalMessageTemplate":{"type":"string","description":"General message template prepended to all room messages. Use {roominstructions} placeholder to inject the room-specific message.","example":"Follow the room instructions: {roominstructions}."}},"description":"Tenant-level guest portal settings."},"GuestPortalActivatedLanguagesRequest":{"type":"object","properties":{"activatedLanguages":{"type":"array","description":"Language codes activated for the guest portal.","example":["en","no"],"items":{"type":"string","description":"Language codes activated for the guest portal.","example":"[\"en\",\"no\"]"}}},"description":"Request payload for guest portal activated languages."},"GuestPortalLanguageOption":{"type":"object","properties":{"code":{"type":"string","description":"ISO 639-1 language code.","example":"en"},"name":{"type":"string","description":"Language name in English.","example":"English"},"nativeName":{"type":"string","description":"Language native display name.","example":"English"}},"description":"Selectable language option for the guest portal."},"GuestPortalDirectionRoute":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"tenantId":{"type":"string"},"name":{"type":"string"},"active":{"type":"boolean"},"position":{"type":"integer","format":"int32"},"translations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/GuestPortalDirectionTranslation"}},"roomIds":{"type":"array","items":{"type":"string"}},"steps":{"type":"array","items":{"$ref":"#/components/schemas/GuestPortalDirectionStep"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"Reusable, room-assigned guest direction route."},"GuestPortalDirectionStep":{"type":"object","properties":{"id":{"type":"string"},"position":{"type":"integer","format":"int32"},"imageId":{"type":"string"},"translations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/GuestPortalDirectionTranslation"}}}},"GuestPortalDirectionTranslation":{"type":"object","properties":{"titleTemplate":{"type":"string"},"descriptionTemplate":{"type":"string"},"imageAltTextTemplate":{"type":"string"}}},"Gateway":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"gatewayId":{"type":"string"},"isOnline":{"type":"boolean"},"mainPowerInUse":{"type":"boolean"},"lastHeardFrom":{"type":"string","format":"date-time"},"description":{"type":"string"},"name":{"type":"string"},"tenantId":{"type":"string"},"nodes":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/adv_payload_t"}},"node_last_heard_from":{"type":"object","additionalProperties":{"type":"string","format":"date-time"}},"isNodeOnline":{"type":"object","additionalProperties":{"type":"boolean"}},"version":{"type":"string"},"nrf_version":{"type":"integer","format":"int32"},"firmwareUpdate":{"type":"boolean"},"downloadedNodeVersion":{"type":"integer","format":"int32"},"downloadedNrfVersion":{"type":"integer","format":"int32"},"isUpdating":{"type":"boolean"},"progress":{"type":"integer","format":"int32"},"gw_state":{"type":"integer","format":"int32"},"connectedToNodeId":{"type":"string"},"pendingNodeId":{"type":"string"},"gatewayStateVersion":{"type":"integer","format":"int32"},"gatewayStateSeq":{"type":"integer","format":"int64"},"gatewayStateAgeMs":{"type":"integer","format":"int64"},"scanRunning":{"type":"boolean"},"connectTaskExists":{"type":"boolean"},"activeConnectionExists":{"type":"boolean"},"activeUuidValid":{"type":"boolean"},"smartplug_state":{"type":"string"},"connected":{"type":"boolean"},"tenantOwner":{"type":"string"},"ready":{"type":"boolean"}}},"Event":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"message":{"$ref":"#/components/schemas/server_message_t"},"reason":{"type":"string"},"bookingId":{"type":"string"}}},"EmailBrandingSettings":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"tenantId":{"type":"string","description":"Tenant id owning these settings","example":"tenant123"},"name":{"type":"string","description":"Display name shown in the email header/footer","example":"Solvotix"},"logoUrl":{"type":"string","description":"Logo URL shown in the email header","example":"https://cdn.example.com/logo.png"},"website":{"type":"string","description":"Website URL shown in the email header","example":"https://www.solvotix.org"},"addressLine":{"type":"string","description":"Address line shown in the email footer","example":"Nordre Sønset 18, 3158 Andebu, Norway"},"supportEmail":{"type":"string","description":"Support email shown in the email footer","example":"support@solvotix.org"},"supportPhone":{"type":"string","description":"Support phone shown in the email footer","example":"+47 22 55 88 99"}},"description":"Per-tenant email branding configuration."},"CleaningProgramSettings":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"updateCheckInTimeOnClean":{"type":"boolean","description":"Whether cleaning updates should also update check-in time.","example":false},"showBookings":{"type":"boolean","description":"Whether bookings should be visible in the cleaning room view.","example":true},"showBookingCode":{"type":"boolean","description":"Whether booking room codes should be visible in the cleaning room view.","example":true},"earliestStartHour":{"type":"integer","description":"Earliest hour a guest should be granted access, using 24-hour format.","format":"int32","example":8},"earliestStartMinute":{"type":"integer","description":"Earliest minute a guest should be granted access.","format":"int32","example":0}},"description":"Tenant-level configuration for the cleaning program."},"CleaningRoom":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"roomId":{"type":"string","description":"Room identifier","example":"room-101"},"roomName":{"type":"string","description":"Display name of the room","example":"Room 101"},"categoryId":{"type":"string","description":"Category identifier for the room","example":"deluxe"},"sensorIds":{"type":"array","description":"Sensors assigned to the room","items":{"type":"string","description":"Sensors assigned to the room"}},"cleaned":{"type":"boolean","description":"Whether the room is currently marked as cleaned"},"cleanedAt":{"type":"string","description":"When the room was marked as cleaned","format":"date-time"},"cleanedByUserId":{"type":"string","description":"User id of the staff member who last updated the cleaning status"},"occupied":{"type":"boolean","description":"Whether the room is currently occupied"},"bookings":{"type":"array","description":"Bookings relevant to the tenant's current date for this room","items":{"$ref":"#/components/schemas/CleaningRoomBooking"}}},"description":"Cleaning status snapshot for a room."},"CleaningRoomBooking":{"type":"object","properties":{"bookingId":{"type":"string","description":"Booking identifier","example":"booking-123"},"guestName":{"type":"string","description":"Booking guest name","example":"Jane Doe"},"email":{"type":"string","description":"Booking email","example":"jane@example.com"},"phone":{"type":"string","description":"Booking phone","example":"+4512345678"},"roomCode":{"type":"string","description":"Room code assigned to the booking","example":"1234"},"start":{"type":"string","description":"Booking start time","format":"date-time"},"end":{"type":"string","description":"Booking end time","format":"date-time"},"arrivingToday":{"type":"boolean","description":"Whether the booking arrives on the tenant's current date"},"leavingToday":{"type":"boolean","description":"Whether the booking leaves on the tenant's current date"},"stayingToday":{"type":"boolean","description":"Whether the booking stays on the tenant's current date"},"checkedIn":{"type":"boolean","description":"Whether the booking is currently checked in"},"checkedOut":{"type":"boolean","description":"Whether the booking is currently checked out"}},"description":"Booking details relevant to the cleaning room view."},"CleaningStatusUpdateRequest":{"type":"object","properties":{"cleaned":{"type":"boolean","description":"Set to true when a room has been cleaned, false to reset it.","example":true}},"description":"Request payload for updating a room cleaning status."},"RoomCodeEntry":{"type":"object","properties":{"code":{"type":"string"},"taken":{"type":"boolean"},"takenAt":{"type":"string","format":"date-time"},"takenByBookingId":{"type":"string"}}},"Rooms":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"roomId":{"type":"string"},"roomName":{"type":"string"},"categoryId":{"type":"string"},"sensorIds":{"type":"array","items":{"type":"string"}},"codes":{"type":"array","items":{"$ref":"#/components/schemas/RoomCodeEntry"}}}},"Categories":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"categoryId":{"type":"string"},"name":{"type":"string"}}},"Booking":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"email":{"type":"string"},"phone":{"type":"string"},"guestName":{"type":"string"},"bookingId":{"type":"string"},"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"},"arrived":{"type":"boolean"},"checkedIn":{"type":"boolean"},"checkedOut":{"type":"boolean"},"externalId":{"type":"string"},"roomId":{"type":"string"},"source":{"type":"string"},"codeSent":{"type":"string","format":"date-time"},"externalReferenceIds":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"messageSent":{"type":"boolean"},"roomCode":{"type":"string"}}},"AutomationTrigger":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"tenantId":{"type":"string","description":"Tenant id for the trigger scope","example":"tenant123"},"triggerKey":{"type":"string","description":"Trigger key","example":"on_day_of_arrival"},"recipient":{"type":"string","description":"Recipient of the automated message (email address or phone number)","example":"manager@example.com"},"sendToBookingGuest":{"type":"boolean","description":"Whether to deliver to the booking guest (uses guest contact details)"},"sendSms":{"type":"boolean","description":"Whether to send an SMS for this trigger"},"sendEmail":{"type":"boolean","description":"Whether to send an email for this trigger"},"message":{"type":"string","description":"Message that will be emitted (legacy/fallback)","example":"Welcome message sent on arrival day"},"smsMessage":{"type":"string","description":"SMS message content","example":"Welcome! Your room is ready."},"emailMessage":{"type":"string","description":"Email message content","example":"Welcome to the building. Your room is ready."},"title":{"type":"string","description":"Title/subject for the email","example":"Welcome to the building"},"timeOfDay":{"type":"string","description":"Time of day in HH:mm (used for day-based triggers)","example":"08:00"},"modifiedDate":{"type":"string","description":"Date the trigger was last modified","format":"date-time","example":"2023-01-02T10:15:00Z"}},"description":"Persisted automation trigger for automated messages."},"AutomationTriggerRequest":{"required":["recipient","triggerKey"],"type":"object","properties":{"triggerKey":{"type":"string","description":"Trigger key","example":"on_day_of_arrival"},"recipient":{"type":"string","description":"Recipient of the automated message (email address or phone number)","example":"manager@example.com"},"sendToBookingGuest":{"type":"boolean","description":"Whether to deliver to the booking guest (derives contact from booking)"},"sendSms":{"type":"boolean","description":"Send SMS when trigger fires"},"sendEmail":{"type":"boolean","description":"Send email when trigger fires"},"title":{"type":"string","description":"Subject/title for email notifications","example":"Welcome to the building"},"smsMessage":{"type":"string","description":"SMS body to send on trigger","example":"Welcome! Your room is ready."},"emailMessage":{"type":"string","description":"Email body to send on trigger","example":"Welcome to the building. Your room is ready."},"message":{"type":"string","description":"Fallback/general message (used if channel-specific message missing)","example":"Welcome message sent on arrival day"},"timeOfDay":{"type":"string","description":"Time of day in HH:mm (required for day-based triggers)","example":"08:00"}},"description":"Request for creating an automation trigger for messages."},"AISettings":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"tenantId":{"type":"string","description":"Tenant id for the settings scope.","example":"tenant123"},"instructions":{"type":"string","description":"Instructions used to guide AI behavior.","example":"Be concise, polite, and focus on check-in support."}},"description":"Tenant-level AI settings."},"ThermostatWifiRequest":{"type":"object","properties":{"ssid":{"type":"string"},"password":{"type":"string"},"gw_id":{"type":"string"}}},"ThermostatTargetTemperatureRequest":{"type":"object","properties":{"temperature":{"type":"number","format":"double"},"gw_id":{"type":"string"}}},"SmartLockCodesRequest":{"type":"object","properties":{"codes":{"type":"array","description":"Access codes (4-7 digits each).","example":["1234","98765"],"items":{"type":"string","description":"Access codes (4-7 digits each).","example":"[\"1234\",\"98765\"]"}}},"description":"Request payload containing smart lock access codes."},"SensorActionRequest":{"type":"object","properties":{"action":{"type":"string"},"subAction":{"type":"string"},"value":{"type":"string"},"userId":{"type":"string"},"gw_id":{"type":"string"}},"description":"Action request payload"},"NodeUpdateRequest":{"type":"object","properties":{"version":{"type":"string"}}},"NodeUpdateStartResponse":{"type":"object","properties":{"sensorId":{"type":"string"},"gatewayId":{"type":"string"},"version":{"type":"string"},"filename":{"type":"string"},"status":{"type":"string"}}},"GuestCheckoutResponse":{"type":"object","properties":{"bookingId":{"type":"string"},"roomId":{"type":"string"},"checkedIn":{"type":"boolean"},"checkedOut":{"type":"boolean"},"checkInTime":{"type":"string"},"checkoutTime":{"type":"string"},"roomMarkedDirty":{"type":"boolean"}}},"AIChatMessage":{"type":"object","properties":{"role":{"type":"string","description":"Sender role for the message.","example":"assistant"},"text":{"type":"string","description":"Message text.","example":"Hello, how can I help with your booking?"},"createdDate":{"type":"string","description":"When the message was created.","format":"date-time"}},"description":"Single message within an AI chat thread."},"AIChatThread":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"tenantId":{"type":"string","description":"Tenant id for thread scope.","example":"tenant123"},"bookingId":{"type":"string","description":"Booking id this thread belongs to.","example":"booking-123"},"sessionId":{"type":"string","description":"Public session id used by the chat API.","example":"a6b8d9c0-3a0d-4d1d-8f69-8d6310de5d8a"},"messages":{"type":"array","description":"Messages currently stored in the thread.","items":{"$ref":"#/components/schemas/AIChatMessage"}}},"description":"Persisted AI chat thread linked to a booking."},"GuestPortalDirectionImage":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"tenantId":{"type":"string"},"fileName":{"type":"string"},"contentType":{"type":"string"},"encodedImage":{"type":"string","writeOnly":true},"sizeBytes":{"type":"integer","format":"int64"},"width":{"type":"integer","format":"int32"},"height":{"type":"integer","format":"int32"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"RoomCodeResponse":{"type":"object","properties":{"code":{"type":"string"},"room":{"$ref":"#/components/schemas/Rooms"}}},"RoomCodeResyncResponse":{"type":"object","properties":{"roomsScanned":{"type":"integer","description":"Total rooms inspected.","format":"int32"},"roomsWithCodes":{"type":"integer","description":"Rooms that had at least one valid stored room code.","format":"int32"},"codeCapableLocks":{"type":"integer","description":"Code-capable room lock links inspected.","format":"int32"},"codesConsidered":{"type":"integer","description":"Valid room-code values considered across rooms.","format":"int32"},"invalidCodesSkipped":{"type":"integer","description":"Invalid stored room-code values skipped.","format":"int32"},"addCommandsSubmitted":{"type":"integer","description":"Smart-lock add-code command requests submitted after duplicate/pending checks.","format":"int32"}},"description":"Summary of a room code resync run."},"BookingMessageRequest":{"type":"object","properties":{"email":{"type":"string","description":"Override email for the guest","example":"guest@example.com"},"phone":{"type":"string","description":"Override phone number for the guest","example":"+4712345678"},"triggerId":{"type":"string","description":"Optional automation trigger id to use. Defaults to the first booking-guest trigger for the tenant."},"smsMessage":{"type":"string","description":"Optional override for the SMS body used when sending."},"emailMessage":{"type":"string","description":"Optional override for the email body used when sending."},"subject":{"type":"string","description":"Optional override for the email subject."}},"description":"Request payload for sending an automated message to a booking guest."},"BookingMessagePreview":{"type":"object","properties":{"subject":{"type":"string"},"emailBody":{"type":"string"},"smsBody":{"type":"string"},"channels":{"type":"array","items":{"type":"string"}}},"description":"Preview of the automated message that will be sent for a booking."},"BookingMessagePreviewResponse":{"type":"object","properties":{"status":{"type":"string","description":"Status of the preview request","example":"SENT","enum":["SENT","BOOKING_NOT_FOUND","NO_TRIGGER_CONFIGURED","NO_RECIPIENT"]},"message":{"type":"string","description":"Human friendly message for the current status"},"preview":{"$ref":"#/components/schemas/BookingMessagePreview"}},"description":"Preview response including status and rendered content."},"BookingRoomCodeRequest":{"type":"object","properties":{"code":{"type":"string","description":"Room access code (4-7 digits).","example":"1234"}},"description":"Request payload for assigning a specific room code to a booking."},"ApiUser":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"name":{"type":"string"},"tenantId":{"type":"string"},"enabled":{"type":"boolean"},"expiresAt":{"type":"string","format":"date-time"},"token":{"type":"string","description":"Plaintext token, returned only when created or rotated"}},"description":"A tenant-bound machine identity"},"AIChatReplyResponse":{"type":"object","properties":{"bookingId":{"type":"string","description":"Booking id connected to the thread.","example":"booking-123"},"userMessage":{"$ref":"#/components/schemas/AIChatMessage"},"assistantMessage":{"$ref":"#/components/schemas/AIChatMessage"},"messages":{"type":"array","description":"Full ordered message history after the send.","items":{"$ref":"#/components/schemas/AIChatMessage"}},"session_id":{"type":"string","description":"Session id of the AI chat thread.","example":"a6b8d9c0-3a0d-4d1d-8f69-8d6310de5d8a"}},"description":"AI chat reply result after sending a message."},"AIChatReplyRequest":{"type":"object","properties":{"text":{"type":"string","description":"Text to send to the AI thread.","example":"Can you draft a check-in email for this guest?"},"session_id":{"type":"string","description":"Session id of the AI chat thread.","example":"a6b8d9c0-3a0d-4d1d-8f69-8d6310de5d8a"}},"description":"Request payload for sending a message to an AI chat thread."},"TemperatureStatsResponse":{"type":"object","properties":{"averageTempC":{"type":"number","description":"Average temperature in Celsius, when available","format":"double","example":21.4},"lowestTempC":{"type":"number","description":"Lowest temperature in Celsius, when available","format":"double","example":18.1},"highestTempC":{"type":"number","description":"Highest temperature in Celsius, when available","format":"double","example":24.7},"averageMessage":{"type":"string","description":"Display message for average temperature","example":"Coming soon"},"lowestMessage":{"type":"string","description":"Display message for lowest temperature","example":"Coming soon"},"highestMessage":{"type":"string","description":"Display message for highest temperature","example":"Coming soon"}},"description":"Latest aggregate temperature statistics for the current tenant."},"TemperatureSensorHistory":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"sensorId":{"type":"string","description":"Sensor id for this reading","example":"A1B2C3D4E5F60708"},"temperatureC":{"type":"number","description":"Temperature in Celsius","format":"double","example":21.7}},"description":"Periodic temperature reading for a specific sensor."},"SmartLockOperationPackage":{"type":"object","properties":{"sensorId":{"type":"string","description":"Smart lock sensor ID.","example":"849FAD3A11223344"},"operation":{"type":"string","description":"Requested lock operation.","example":"pulse-open"},"action":{"type":"integer","description":"Protocol action value.","format":"int32","example":33},"subAction":{"type":"integer","description":"Protocol sub-action value.","format":"int32","example":1},"messageId":{"type":"string","description":"Generated message ID in hexadecimal.","example":"A1B2C3D4E5F6"},"packageBase64":{"type":"string","description":"Complete node-core package encoded as Base64."},"packageHex":{"type":"string","description":"Complete node-core package encoded as uppercase hexadecimal."}},"description":"Bluetooth package for a mobile app to forward directly to a smart lock."},"SmartLockCodeSlot":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"deviceId":{"type":"string"},"slotNumber":{"type":"integer","format":"int32"},"code":{"type":"string"},"state":{"type":"integer","format":"int32"},"addedAt":{"type":"string","format":"date-time"},"uploadedToLockAt":{"type":"string","format":"date-time"},"warnedAboutNotUploaded":{"type":"boolean"}}},"NodeFirmwareInfo":{"type":"object","properties":{"filename":{"type":"string"},"version":{"type":"string"},"size":{"type":"integer","format":"int64"},"latest":{"type":"boolean"}}},"MessageHistoryResponse":{"type":"object","properties":{"messages":{"type":"array","items":{"$ref":"#/components/schemas/MessageRecord"}},"newMessages":{"type":"integer","format":"int32"},"latestTimestamp":{"type":"integer","format":"int64"}}},"MessageRecord":{"type":"object","properties":{"recordId":{"type":"string"},"timestamp":{"type":"integer","format":"int64"},"direction":{"type":"string"},"gatewayId":{"type":"string"},"nodeId":{"type":"string"},"messageId":{"type":"string"},"status":{"type":"integer","format":"int32"},"delivered":{"type":"boolean"},"message":{"$ref":"#/components/schemas/server_message_t"}}},"RoomNameResponse":{"type":"object","properties":{"roomName":{"type":"string"}}},"BookingValidityResponse":{"type":"object","properties":{"status":{"type":"integer","format":"int32"},"date":{"type":"string"},"checkInTime":{"type":"string"},"checkoutTime":{"type":"string"},"checkedIn":{"type":"boolean"},"checkedOut":{"type":"boolean"}}},"GuestPortalDirectionsResponse":{"type":"object","properties":{"bookingId":{"type":"string"},"roomId":{"type":"string"},"roomName":{"type":"string"},"language":{"type":"string"},"paths":{"type":"array","items":{"$ref":"#/components/schemas/Path"}}}},"Path":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"position":{"type":"integer","format":"int32"},"title":{"type":"string"},"description":{"type":"string"},"steps":{"type":"array","items":{"$ref":"#/components/schemas/Step"}}}},"Step":{"type":"object","properties":{"id":{"type":"string"},"position":{"type":"integer","format":"int32"},"title":{"type":"string"},"description":{"type":"string"},"imageAltText":{"type":"string"},"imageId":{"type":"string"},"imageUrl":{"type":"string"}}},"AIModuleActivatedResponse":{"type":"object","properties":{"activated":{"type":"boolean"}}},"GatewayMeteringRecord":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"tenantId":{"type":"string"},"gatewayId":{"type":"string"},"messageIdHex":{"type":"string"},"formatVersion":{"type":"integer","format":"int32"},"subAction":{"type":"integer","format":"int32"},"recordType":{"type":"string","enum":["FIVE_MINUTE","HOURLY_FALLBACK","CUMULATIVE_CHECKPOINT","CONSUMPTION_CHANGED"]},"sequence":{"type":"integer","format":"int64"},"intervalPulses":{"type":"integer","format":"int64"},"durationMs":{"type":"integer","format":"int64"},"totalPulses":{"type":"integer","format":"int64"},"stored":{"type":"boolean"},"cumulative":{"type":"boolean"},"receivedAt":{"type":"string","format":"date-time"}},"description":"An immutable metering record received from a gateway."},"PageGatewayMeteringRecord":{"type":"object","properties":{"totalPages":{"type":"integer","format":"int32"},"totalElements":{"type":"integer","format":"int64"},"pageable":{"$ref":"#/components/schemas/PageableObject"},"size":{"type":"integer","format":"int32"},"content":{"type":"array","items":{"$ref":"#/components/schemas/GatewayMeteringRecord"}},"number":{"type":"integer","format":"int32"},"sort":{"$ref":"#/components/schemas/SortObject"},"numberOfElements":{"type":"integer","format":"int32"},"first":{"type":"boolean"},"last":{"type":"boolean"},"empty":{"type":"boolean"}}},"PageableObject":{"type":"object","properties":{"pageNumber":{"type":"integer","format":"int32"},"pageSize":{"type":"integer","format":"int32"},"offset":{"type":"integer","format":"int64"},"sort":{"$ref":"#/components/schemas/SortObject"},"paged":{"type":"boolean"},"unpaged":{"type":"boolean"}}},"SortObject":{"type":"object","properties":{"sorted":{"type":"boolean"},"empty":{"type":"boolean"},"unsorted":{"type":"boolean"}}},"GatewayQueueMessage":{"type":"object","properties":{"nodeId":{"type":"string"},"createdDate":{"type":"string","format":"date-time"},"attemptedDeliveredDate":{"type":"string","format":"date-time"},"lastGatewayTried":{"type":"string"},"reasonLabel":{"type":"string"},"reason":{"type":"string"},"reasonDetails":{"type":"string"},"routeMode":{"type":"string"},"selectedGateway":{"type":"string"},"selectedGatewayState":{"type":"string"},"connectBlockReason":{"type":"string"},"eligibleGatewayCount":{"type":"integer","format":"int32"},"eligibleGateways":{"type":"array","items":{"type":"string"}},"messageId":{"type":"string"},"action":{"type":"integer","format":"int32"},"subaction":{"type":"integer","format":"int32"}}},"SolvotixEvent":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier","example":"abc123"},"userId":{"type":"string","description":"The id of the user who created this object.","example":"user456"},"createdDate":{"type":"string","description":"Date the object was created","format":"date-time","example":"2023-01-01T00:00:00Z"},"sensor_uuid":{"type":"string"},"roomId":{"type":"string"},"text":{"type":"string"},"reason":{"type":"string"},"action":{"type":"integer","format":"int32"},"sub_action":{"type":"integer","format":"int32"},"data":{"type":"object","additionalProperties":{"type":"string"}},"timestamp":{"type":"integer","format":"int32"},"nodeTime":{"type":"string","format":"date-time"}}},"AutomationCapability":{"type":"object","properties":{"title":{"type":"string","description":"Category of the automation","example":"Temperature Control"},"actions":{"type":"array","description":"Actions carried out by this automation","items":{"type":"string","description":"Actions carried out by this automation"}}},"description":"Automation capability with its name and the actions it performs."},"AutomationMessage":{"type":"object","properties":{"key":{"type":"string","description":"Message key or trigger","example":"temperature_threshold"},"message":{"type":"string","description":"Human readable message sent when the trigger fires"}},"description":"Template for an automated notification emitted by building automation flows."},"AutomationTriggerDefinition":{"type":"object","properties":{"key":{"type":"string","description":"Trigger key","example":"on_day_of_arrival"},"description":{"type":"string","description":"Human readable description","example":"On day of arrival"},"requiresTimeOfDay":{"type":"boolean","description":"Whether the trigger requires a time of day"}},"description":"Supported automation trigger definition."},"AIChatThreadSummary":{"type":"object","properties":{"bookingId":{"type":"string","description":"Booking id connected to the thread.","example":"booking-123"},"userId":{"type":"string","description":"User id that created the thread.","example":"user-123"},"createdDate":{"type":"string","description":"When the thread was created.","format":"date-time"},"messageCount":{"type":"integer","description":"Number of messages in the thread.","format":"int32","example":4},"lastMessageText":{"type":"string","description":"Text from the latest message in the thread.","example":"Can you help me with check-in?"},"lastMessageRole":{"type":"string","description":"Role of the latest message sender.","example":"assistant"},"session_id":{"type":"string","description":"Session id of the AI chat thread.","example":"a6b8d9c0-3a0d-4d1d-8f69-8d6310de5d8a"}},"description":"Summary view of an AI chat thread."}},"securitySchemes":{"bearerAuth":{"type":"http","description":"Enter a Solvotix API token beginning with `sat_`, or a Firebase ID token.\n\nIn Swagger UI, enter only the token value. Swagger adds the `Bearer` prefix automatically.\n\nSolvotix API token example:\n\n```\nsat_<API_TOKEN>\n```\n\nThe resulting request header is:\n\n```\nAuthorization: Bearer sat_<API_TOKEN>\n```\n\nAPI tokens are tenant-bound and do not use Firebase. Firebase ID tokens remain supported\nfor interactive users.\n","scheme":"bearer","bearerFormat":"Solvotix API token (sat_...) or Firebase ID token"}}}}