{"paths":{"\/api\/organization\/invite":{"post":{"tags":["Organization"],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/InviteUserToOrganizationData"},"examples":{"InviteUserToOrganizationData":{"$ref":"#\/components\/examples\/InviteUserToOrganizationData"}}}}},"operationId":"postApiOrganizationInvite","description":"Generates and sends an invitation link for the given organization to the given user email with a default rolw of ReadOnlyUser.","summary":"Sends an organization invite to the given email.","security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/OrgInviteResponse"},"examples":{"OrgInviteResponse":{"$ref":"#\/components\/examples\/OrgInviteResponse"}}}}}}}},"\/api\/util\/pl-hardware-info":{"get":{"summary":"Indicates to clients whether or not they are hives serving a webserver.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/DeployedOnHiveResponse"}}}}},"description":"Returns a JSON object where the field 'runningOnHive' will be true if, on the server, the PL_HIVE_REVISION environment variable\nis set to a number greater than or equal to zero.","operationId":"getApiUtilPl-hardware-info","tags":["Utility"]}},"\/api\/authgroup\/decline":{"post":{"description":"Rejects an invitation based on AuthGroup invite ID.\nInvitation is deleted.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"},"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}}}}}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiAuthgroupDecline","tags":["Auth Groups"],"summary":"Declines an AuthGroup invitation","requestBody":{"required":true,"content":{"application\/json":{"examples":{"DeclineOrganizationInvite":{"$ref":"#\/components\/examples\/DeclineOrganizationInvite"}},"schema":{"$ref":"#\/components\/schemas\/DeclineOrganizationInvite"}}}}}},"\/api\/binding\/link":{"post":{"description":"Associates an existing `Binding` node with an already-uploaded file without re-uploading the file data. The user must have write access to the target file, and linking files across AuthGroups is not allowed.","responses":{"200":{"description":"OK","content":{"application\/json":{"examples":{"BindingUploadResponse":{"$ref":"#\/components\/examples\/BindingUploadResponse"}},"schema":{"$ref":"#\/components\/schemas\/BindingUploadResponse"}}}}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiBindingLink","tags":["Bindings"],"summary":"Link a binding node to an existing file.","requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/LinkFileData"},"examples":{"LinkFileData":{"$ref":"#\/components\/examples\/LinkFileData"}}}}}}},"\/api\/image\/{imageID}":{"delete":{"description":"DEPRECATED: Delete an image node via the normal GraphQL endpoint or a QuantumSync operation. This route has been deprecated in favor of: POST \/api\/graphql.\n\nDelete the image with the given quantum node ID.\nIf authorized and the given image is found, it will be deleted from the file store and the database.\nThe ID provided must be the ID of the `Image` Quantum node, NOT the name of the image.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"},"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}}}}}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"deleteApiImageByImageID","tags":["Images"],"summary":"Delete an image.","parameters":[{"name":"imageID","required":true,"schema":{"type":"string"},"in":"path"}],"deprecated":true}},"\/api\/comfort\/adjustment":{"post":{"responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ComfortControllerComfortAdjustmentResponse"}}}}},"requestBody":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ComfortControllerComfortAdjustmentRequest"}}},"required":true},"operationId":"postApiComfortAdjustment","description":"","summary":"Adjusts the comfort model of the provided zone group to make the space hotter or colder.","security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"tags":["Comfort"]}},"\/api\/quantumsync":{"get":{"summary":"QuantumSync API over websockets","tags":["Quantum Sync"],"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"getApiQuantumsync","description":"# Custom QuantumSync\/ShadowClient API:\n\n## Error and retry convention\nClients own retry policy from stable error-code categories. Request and domain errors (441x and 443x–445x)\nrequire a corrected request rather than a blind replay. Backend errors (446x) may be retried with bounded\nexponential backoff; internal errors (447x) should be surfaced rather than automatically replayed. A sync\nresponse with an unconfirmed `transactionOutcome` must be reconciled before replaying the request.\n\n## QuantumSync QuantumSyncRequestTypes:\n### `start`: authenticates and specifies an initial context. note: the id for start and updateContext is used to key a Dictionary[String:AnyRootedQuantumGraph]\nex)\n```json\n{\n    \"type\": \"start\",\n    \"id\": \"site\",\n    \"appType\":\"BuildingStudio\"\n    \"payload\": {\n        \"authToken\": \"REDACTED\",\n        \"ID\": \"ACAF9E07-5031-4BC2-A8C5-D2C6598D83DE\",\n        \"from\": \"Site\",\n        \"include\": [\n            \"Site\",\n            \"Building\",\n            \"Floor\",\n            \"Zone\"\n    ]\n    }\n}\n```\n\n### `updateContext`: change the current context \/ graph in memory.\nnote: the previous graph\/context can be returned from memory by using updateContext id: <previous id used in start or updateContext>.\nUp to 5 contexts can be loaded before prevention (error) or deleteContext message needed to free up loaded count to load a new context.\n\nex)\n```json\n{\n    \"type\": \"updateContext\",\n    \"id\": \"building\",\n    \"payload\": {\n        \"ID\": \"ACAF9E07-5031-4BC2-A8C5-D2C6598D83DE\",\n        \"from\": \"Building\",\n        \"include\": [\n            \"Site\",\n            \"Floor\",\n            \"Zone\",\n            \"Image\",\n            \"Surface\",\n            \"Adjacency\",\n            \"System\",\n            \"Quanta\",\n            \"Manufacturer\",\n            \"Equipment\",\n            \"Property\",\n            \"Location\"\n        ]\n    }\n}\n```\n\n### `deleteContext`: delete\/clear the context out of memory.\nex)\n```json\n{\n    \"type\": \"deleteContext\",\n    \"id\": \"building\",\n    \"payload\": {}\n}\n```\n\n### `sync`: create\/update\/delete QuantumObjects via a FlatGraphDiff as the payload.\n\nExecuting concurrent sync's is not allowed. If there is a sync already processing and another sync is requested before the response of the first sync is sent, then the 2nd symc will return an error saying there is a sync currently processing, and to wait until its finished before requesting another sync.\n\nex)\n```json\n{\n    \"type\": \"sync\",\n    \"id\": \"F5B6BC51-B991-440C-8ACB-B55C65FD7C24\",\n    \"payload\": {\n        \"added\": [],\n        \"updated\": [\n            {\n                \"description\": null,\n                \"ID\": \"80187B0B-CEEB-488A-A9B7-06784CFB0464\",\n                \"longitude\": null,\n                \"locationPropertyID\": null,\n                \"elevation\": null,\n                \"latitude\": null,\n                \"address\": null,\n                \"timeZone\": null,\n                \"siteIDs\": [\n                    \"7E7E04B4-8F32-4585-839D-E0EFC7BB3544\"\n                ],\n                \"weatherSourceIDs\": [],\n                \"locked\": false,\n                \"authGroupID\": \"A1EBEC59-D887-473F-A5C5-7CBD4ADB2556\",\n                \"__typename\": \"Location\",\n                \"name\": \"foo\"\n            }\n        ],\n        \"removed\": []\n    }\n}\n```\n\n### `syncEphemeral`: create\/update\/delete QuantumObjects via a FlatGraphDiff as the payload, do not write them to the database,\nand discard them at the end of the websocket session.\nex)\n```json\n{\n    \"type\": \"syncEphemeral\",\n    \"id\": \"F5B6BC51-B991-440C-8ACB-B55C65FD7C24\",\n    \"payload\": {\n        \"added\": [],\n        \"updated\": [\n            {\n                \"description\": null,\n                \"ID\": \"80187B0B-CEEB-488A-A9B7-06784CFB0464\",\n                \"longitude\": null,\n                \"locationPropertyID\": null,\n                \"elevation\": null,\n                \"latitude\": null,\n                \"address\": null,\n                \"timeZone\": null,\n                \"siteIDs\": [\n                    \"7E7E04B4-8F32-4585-839D-E0EFC7BB3544\"\n                ],\n                \"weatherSourceIDs\": [],\n                \"locked\": false,\n                \"authGroupID\": \"A1EBEC59-D887-473F-A5C5-7CBD4ADB2556\",\n                \"__typename\": \"Location\",\n                \"name\": \"foo\"\n            }\n        ],\n        \"removed\": []\n    }\n}\n```\n\n### `propertyHistory`: Get history data for a property & listen for updates.\nex)\n```json\n{\n    \"type\": \"propertyHistory\",\n    \"id\": \"propertyHistory1\",\n    \"payload\": {\n        \"propertyIDs\": [\"E585C3B8-F1E5-40D0-83A7-658B248DF103\"],\n        \"startAt\": \"2024-01-01T00:00:00Z\",\n        \"endAt\": \"2024-01-01T01:00:00Z\",\n        \"resample\": {\n            \"interval\": {\n                \"to\": \"minutely\",\n                \"reduceUsing\": \"avg\",\n                \"fillUsing\": \"interpolate\"\n        },\n        \"pollIntervalSec\": 60\n    }\n}\n\n### `propertyHistoryStop`: Stop listening for property history updates\nex)\n```json\n{\n    \"type\": \"propertyHistoryStop\",\n    \"id\": \"propertyHistory1\"\n}\n```\n\n### `writeHistory`: Write historical data to a property\nex)\n```json\n{\n    \"type\": \"writeHistory\",\n    \"id\": \"writeHistory\",\n    \"payload\": {\n        \"histories\": [\n            {\n                \"propertyID\": \"E585C3B8-F1E5-40D0-83A7-658B248DF103\",\n                \"time\": \"2024-01-01T00:00:00Z\",\n                \"value\": 5.0\n            }\n        ]\n    }\n}\n```"}},"\/api\/auth\/password\/reset":{"post":{"responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"},"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}}}}}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/RequestResetPasswordData"},"examples":{"RequestResetPasswordData":{"$ref":"#\/components\/examples\/RequestResetPasswordData"}}}}},"deprecated":true,"operationId":"postApiAuthPasswordReset","description":"DEPRECATED: This endpoint is not available when Keycloak authentication is turned on and will be removed in the future.\n\nInitiates a password reset for the given user, sending them an email with a password reset link authenticated to their\naccount only.","summary":"Initiates a password reset.","tags":["Authentication"]}},"\/api\/authgroup\/remove\/{authGroupID}\/{userID}":{"post":{"description":"Remove a User from an AuthGroup. Users must have PrivilegedUser or greater permissions to remove another user.\nUser can only remove other users with permissions less than or equal to their own permissions. For example: PrivilegedUsers\ncannot remove SuperUsers.","responses":{"200":{"content":{"application\/json":{"examples":{"AuthGroupRemovalResponse":{"$ref":"#\/components\/examples\/AuthGroupRemovalResponse"}},"schema":{"$ref":"#\/components\/schemas\/AuthGroupRemovalResponse"}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiAuthgroupRemoveByAuthGroupIDByUserID","parameters":[{"required":true,"schema":{"type":"string"},"name":"authGroupID","in":"path"},{"required":true,"schema":{"type":"string"},"name":"userID","in":"path"}],"summary":"Removes user from Auth Group","tags":["Auth Groups"]}},"\/api\/auth\/keys":{"get":{"description":"Returns the public keys used to sign the JSON web tokens so their authenticity can be verified. These are served in the\nstandard JSON Web Key format: https:\/\/www.rfc-editor.org\/rfc\/rfc7517","tags":["Authentication"],"operationId":"getApiAuthKeys","responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/JWKS"}}},"description":"OK"}},"summary":"Returns the public keys used to sign PassiveLogic JSON web tokens."}},"\/api\/account\/user\/delete":{"delete":{"responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"},"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}}}}}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"tags":["Account"],"description":"Deletes the requesting user if they are not a SuperUser within any organizations.\nOtherwise prompts them to transfer ownership or delete the org before making this request.","summary":"Deletes the user making this request.","operationId":"deleteApiAccountUserDelete"}},"\/api\/graphql":{"get":{"externalDocs":{"description":"GraphQL documentation.","url":"https:\/\/graphql.org\/learn\/serving-over-http\/#get-request"},"description":"Serves the GraphQL API. For more details, see [graphql.org](https:\/\/graphql.org\/learn\/serving-over-http\/#get-request)","responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GraphQLResult"}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"getApiGraphql","parameters":[{"required":true,"name":"query","in":"query","schema":{"type":"string"}},{"required":false,"name":"operationName","in":"query","schema":{"type":"string","nullable":true}},{"required":false,"example":{},"name":"variables","in":"query","schema":{"nullable":true,"additionalProperties":{"type":"boolean"},"type":"object"}}],"tags":["GraphQL"],"summary":"GraphQL API"},"post":{"externalDocs":{"url":"https:\/\/graphql.org\/learn\/serving-over-http\/#post-request","description":"GraphQL documentation."},"description":"Serves the GraphQL API. For more details, see [graphql.org](https:\/\/graphql.org\/learn\/serving-over-http\/#post-request)","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GraphQLResult"}}}}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiGraphql","tags":["GraphQL"],"summary":"GraphQL API","requestBody":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GraphQLRequest"}}},"required":true}}},"\/api\/auth\/register\/initiate":{"post":{"description":"DEPRECATED: This endpoint is not available when Keycloak authentication is turned on and will be removed in the future.\n\nInitiates the PassiveLogic registration process by sending an email to the given user. A user is not stored in the system\nat this point in the process - instead this serves as the initial step proving that a uer has access to the email they\nare signing up with.\n\nEmail will contain a link that holds a signed JWT from us. That JWT is later used to validate and complete registration.\n\nNo authentication required.","responses":{"200":{"description":"OK","content":{"application\/json":{"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}},"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"}}}}},"operationId":"postApiAuthRegisterInitiate","tags":["Authentication"],"summary":"Sends an email to begin email verification and registration.","deprecated":true,"requestBody":{"required":true,"content":{"application\/json":{"examples":{"UserRegistrationInitiation":{"$ref":"#\/components\/examples\/UserRegistrationInitiation"}},"schema":{"$ref":"#\/components\/schemas\/UserRegistrationInitiation"}}}}}},"\/api\/datasync":{"get":{"deprecated":true,"description":"Refer to the \/api\/quantumsync route documentation for full details. This route will be removed in the next major version.","security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"getApiDatasync","tags":["Quantum Sync"],"summary":"QuantumSync API Legacy Route. Use \/api\/quantumsync instead."}},"\/api\/organization\/update-image":{"post":{"description":"Verifies that the user making the request has sufficient permissions to change the image for the organization.\nTakes in a new image as the new organization image. Creates or replaces the previously stored image.","responses":{"200":{"description":"OK","content":{"application\/json":{"examples":{"ImageUploadResponse":{"$ref":"#\/components\/examples\/ImageUploadResponse"}},"schema":{"$ref":"#\/components\/schemas\/ImageUploadResponse"}}}}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiOrganizationUpdate-image","tags":["Organization"],"summary":"Updates the organization's image","requestBody":{"required":true,"content":{"application\/json":{"examples":{"OrganizationImageData":{"$ref":"#\/components\/examples\/OrganizationImageData"}},"schema":{"$ref":"#\/components\/schemas\/OrganizationImageData"}}}}}},"\/api\/auth\/api-keys\/{apiKeyID}":{"patch":{"description":"","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ApiKeyResponse"},"examples":{"ApiKeyResponse":{"$ref":"#\/components\/examples\/ApiKeyResponse"}}}}}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"patchApiAuthApi-keysByApiKeyID","tags":["API Keys"],"summary":"Updates API key metadata. API key role may only be reduced.","parameters":[{"in":"path","name":"apiKeyID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application\/json":{"examples":{"UpdateApiKeyRequest":{"$ref":"#\/components\/examples\/UpdateApiKeyRequest"}},"schema":{"$ref":"#\/components\/schemas\/UpdateApiKeyRequest"}}},"required":true}},"delete":{"description":"","responses":{"204":{"description":"API key revoked."}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"deleteApiAuthApi-keysByApiKeyID","tags":["API Keys"],"summary":"Revokes an API key","parameters":[{"in":"path","name":"apiKeyID","required":true,"schema":{"type":"string"}}]},"get":{"description":"","responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ApiKeyResponse"},"examples":{"ApiKeyResponse":{"$ref":"#\/components\/examples\/ApiKeyResponse"}}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"getApiAuthApi-keysByApiKeyID","tags":["API Keys"],"summary":"Gets API key metadata","parameters":[{"in":"path","name":"apiKeyID","required":true,"schema":{"type":"string"}}]}},"\/api\/auth\/api-key":{"delete":{"description":"Validates that the given token is linked to the currently authenticated user and removes it if so.","responses":{"200":{"content":{"application\/json":{"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}},"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"deleteApiAuthApi-key","tags":["Authentication"],"summary":"Removes a given API key for the user. Deprecated in favor of DELETE \/api\/auth\/api-keys\/:id.","deprecated":true,"requestBody":{"required":true,"content":{"application\/json":{"examples":{"RemoveApiKeyRequest":{"$ref":"#\/components\/examples\/RemoveApiKeyRequest"}},"schema":{"$ref":"#\/components\/schemas\/RemoveApiKeyRequest"}}}}}},"\/api\/organization\/delete":{"post":{"description":"Completely deletes an organization, after verifying that the user making this request has permission to do so.\nSends an email to all other members of the organization informing them that it has been deleted.","responses":{"200":{"content":{"application\/json":{"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}},"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiOrganizationDelete","tags":["Organization"],"summary":"Deletes specified organization.","requestBody":{"content":{"application\/json":{"examples":{"DeleteOrganizationData":{"$ref":"#\/components\/examples\/DeleteOrganizationData"}},"schema":{"$ref":"#\/components\/schemas\/DeleteOrganizationData"}}},"required":true}}},"\/api\/auth\/magic-link-login":{"get":{"description":"DEPRECATED: This endpoint is not available when Keycloak authentication is turned on and will be removed in the future.\n\nRequires a magic login link containing containing `?token=<signed JWT with user email contained>`.\nThis is the secure mode and MUST be used in production.","responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"},"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"getApiAuthMagic-link-login","tags":["Authentication"],"summary":"Logs in a user using Magic Link generated by POST magic-link-generate","deprecated":true}},"\/api\/auth\/api-keys":{"post":{"description":"","responses":{"201":{"content":{"application\/json":{"examples":{"CreatedApiKeyResponse":{"$ref":"#\/components\/examples\/CreatedApiKeyResponse"}},"schema":{"$ref":"#\/components\/schemas\/CreatedApiKeyResponse"}}},"description":"Created"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiAuthApi-keys","tags":["API Keys"],"summary":"Creates an API key for the authenticated user","requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/CreateApiKeyRequest"},"examples":{"CreateApiKeyRequest":{"$ref":"#\/components\/examples\/CreateApiKeyRequest"}}}}}},"get":{"description":"","tags":["API Keys"],"operationId":"getApiAuthApi-keys","security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"summary":"Lists API keys owned by the authenticated user","responses":{"200":{"content":{"application\/json":{"schema":{"items":{"$ref":"#\/components\/schemas\/ApiKeyResponse"},"type":"array"},"examples":{"ArrayApiKeyResponse":{"$ref":"#\/components\/examples\/ArrayApiKeyResponse"}}}},"description":"OK"}}}},"\/api\/authgroup\/invite":{"post":{"description":"Generates and sends an invitation link for the given Site or View (AuthGroup) to the given user email.","responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/AuthGroupInviteResponse"},"examples":{"AuthGroupInviteResponse":{"$ref":"#\/components\/examples\/AuthGroupInviteResponse"}}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiAuthgroupInvite","tags":["Auth Groups"],"summary":"Sends an AuthGroup invite to the given email.","requestBody":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/InviteUserToAuthGroupData"},"examples":{"InviteUserToAuthGroupData":{"$ref":"#\/components\/examples\/InviteUserToAuthGroupData"}}}},"required":true}}},"\/api\/export\/property\/history":{"get":{"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"summary":"Exports CSV formatted property history data","description":"Returns a string representation of property history values in CSV format from the given properties.\nreturns in the following headers:\nTime,Equipment name,Property name,Unit,Value\n\nTime returned in ISO8601 string format","tags":["Export"],"operationId":"getApiExportPropertyHistory","responses":{"200":{"description":"OK","content":{"text\/plain":{"examples":{"String":{"$ref":"#\/components\/examples\/String"}},"schema":{"type":"string"}}}}}}},"\/api\/auth\/login":{"get":{"description":"DEPRECATED: This endpoint is not available when Keycloak authentication is turned on and will be removed in the future.\n\nLogs in a user. If multi-factor authentication is enabled, returns a multi-factor token that can be used to\nsend a multi-factor code to the user. Otherwise, sets auth & refresh tokens as cookies and returns a whoami for the\nlogged in user.\n\nBasic authentication required.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/LoginResponse"},"examples":{"LoginResponse":{"$ref":"#\/components\/examples\/LoginResponse"}}}}}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"getApiAuthLogin","tags":["Authentication"],"summary":"Logs in a user","deprecated":true}},"\/api\/util\/quantumlens":{"post":{"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/QuantumLensResponse"}}}}},"tags":["Utility"],"description":"Returns the status of the QuantumLens application for the current user.\n\n- `appUpdate`: indicates if a new app version is required (true = forced, false = soft, nil = no update)\n- `osUpdate`: indicates if an iOS update is required (true = update required, nil = compatible)\n- `user`: information about the current user\n\nThis response helps the client determine whether the app or iOS requires an update before continuing.","summary":"QuantumLens (iOS) update & compatibility check","operationId":"postApiUtilQuantumlens"}},"\/api\/account\/eula\/accept":{"post":{"description":"DEPRECATED: This endpoint is not available when Keycloak authentication is turned on and will be removed in the future.\nDenotes that the current user has accepted the EULA.\nReturns updated access and refresh tokens.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"},"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}}}}}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiAccountEulaAccept","tags":["Account"],"summary":"Accepts EULA for the current user","deprecated":true}},"\/api\/auth\/email\/change":{"post":{"description":"DEPRECATED: This endpoint is not available when Keycloak authentication is turned on and will be removed in the future.\n\nSends an update email request to the provided new email address.","responses":{"200":{"content":{"application\/json":{"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}},"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiAuthEmailChange","tags":["Authentication"],"summary":"Initiates an email change for the current user.","deprecated":true,"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/UpdatedUserEmail"},"examples":{"UpdatedUserEmail":{"$ref":"#\/components\/examples\/UpdatedUserEmail"}}}}}}},"\/api\/device\/tailscale\/authToken":{"get":{"operationId":"getApiDeviceTailscaleAuthToken","responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/TailscalePostTailnetKeyResponse"}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"summary":"Generates an OAuth token for Tailscale","description":"AutToken generated allowing hive's to enroll in our tailnet.\n\nmTLS authentication required.","tags":["Device Communications"]}},"\/api\/util\/ping":{"get":{"tags":["Utility"],"description":"Use to check webserver health or keep connection alive. Returns pong message.","responses":{"200":{"content":{"text\/plain":{"schema":{"type":"string"},"examples":{"String":{"$ref":"#\/components\/examples\/String"}}}},"description":"OK"}},"operationId":"getApiUtilPing","summary":"Keep-alive ping\/pong route"}},"\/api\/device\/register":{"post":{"description":"Registers a PassiveLogic device to a site and returns the auth group the device now belongs to.\n\nThe caller must prove they are allowed to add the device to the site in one of two ways:\n- Provide a `userJWT` from a user who has access to the site. The device making the request is the one being registered.\n- Provide a `followerSerialNumber` when a leader device is bringing a follower onto its site.\n\nIf the device is already registered, its auth group is updated instead of creating a duplicate registration.\n\nThis call must be made on the device itself over mTLS authentication.\n\nOnly call this endpoint for devices provisioned by a webserver on v9.1.1 or older, where provisioning does not register the device correctly when `initiate` is called. Newer devices are registered automatically and do not need this call.","responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/DeviceRegistrationResponse"},"examples":{"DeviceRegistrationResponse":{"$ref":"#\/components\/examples\/DeviceRegistrationResponse"}}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiDeviceRegister","tags":["Device Communications"],"summary":"Registers a device to a site","requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/DeviceRegistrationRequest"}}}}}},"\/api\/util\/quantumversion":{"get":{"description":"Returns the current version of the Quantum schema in use by this webserver as a semantic version.","operationId":"getApiUtilQuantumversion","responses":{"200":{"content":{"text\/plain":{"schema":{"type":"string"},"examples":{"String":{"$ref":"#\/components\/examples\/String"}}}},"description":"OK"}},"summary":"Returns the Quantum Schema version","tags":["Utility"]}},"\/api\/organization\/join":{"post":{"description":"Adds user to organization based on Organization invite ID.\nRole in organization is determined through Organization invite role, if no role is defined, we default to read-only user.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"},"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}}}}}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiOrganizationJoin","tags":["Organization"],"summary":"Adds a user to an organization","requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/AcceptOrganizationInvite"},"examples":{"AcceptOrganizationInvite":{"$ref":"#\/components\/examples\/AcceptOrganizationInvite"}}}}}}},"\/api\/auth\/whoami":{"get":{"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"summary":"Returns information about the currently logged in user.","description":"Useful to check if a user is authenticated.","tags":["Authentication"],"operationId":"getApiAuthWhoami","responses":{"200":{"content":{"application\/json":{"examples":{"WhoAmIResponse":{"$ref":"#\/components\/examples\/WhoAmIResponse"}},"schema":{"$ref":"#\/components\/schemas\/WhoAmIResponse"}}},"description":"OK"}}}},"\/api\/auth\/magic-link-generate":{"post":{"description":"DEPRECATED: This endpoint is not available when Keycloak authentication is turned on and will be removed in the future.\n\nGenerates magic link containing `?token=<signed JWT with user email contained>` and sends it in an email to the user.\nThis is the secure mode and MUST be used in production.\nThis will not return a link to the client.\n\nIf User does not exist in Database, will throw 400 bad request. If user exists, will return 200 ok.\nThese links can then be used to login from GET magic-link-login.","responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/MagicLinkResponse"}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiAuthMagic-link-generate","tags":["Authentication"],"summary":"Generates a Magic Link to be used to login.","deprecated":true,"requestBody":{"required":true,"content":{"application\/json":{"examples":{"UserRegistrationInitiation":{"$ref":"#\/components\/examples\/UserRegistrationInitiation"}},"schema":{"$ref":"#\/components\/schemas\/UserRegistrationInitiation"}}}}}},"\/api\/auth\/email\/change\/verify":{"get":{"responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"},"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}}}}}},"requestBody":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/UpdatedUserEmail"},"examples":{"UpdatedUserEmail":{"$ref":"#\/components\/examples\/UpdatedUserEmail"}}}},"required":true},"deprecated":true,"operationId":"getApiAuthEmailChangeVerify","description":"DEPRECATED: This endpoint is not available when Keycloak authentication is turned on and will be removed in the future.\n\nVerifies the new email is valid. Updates the user email with the newly validated one.\n\nToken authentication required. Provided by \/api\/auth\/email\/change.","summary":"Verifies and updates new user email","security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"tags":["Authentication"]}},"\/api\/auth\/api-key\/remove":{"delete":{"description":"Validates that the given token is linked to the currently authenticated user and removes it if so.","responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"},"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"deleteApiAuthApi-keyRemove","tags":["Authentication"],"summary":"Removes a given API key for the user. Deprecated in favor of DELETE \/api\/auth\/api-keys\/:id.","deprecated":true,"requestBody":{"required":true,"content":{"application\/json":{"examples":{"RemoveApiKeyRequest":{"$ref":"#\/components\/examples\/RemoveApiKeyRequest"}},"schema":{"$ref":"#\/components\/schemas\/RemoveApiKeyRequest"}}}}},"post":{"description":"Validates that the given token is linked to the currently authenticated user and removes it if so.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"},"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}}}}}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiAuthApi-keyRemove","tags":["Authentication"],"summary":"Removes a given API key for the user. Deprecated in favor of DELETE \/api\/auth\/api-keys\/:id.","deprecated":true,"requestBody":{"required":true,"content":{"application\/json":{"examples":{"RemoveApiKeyRequest":{"$ref":"#\/components\/examples\/RemoveApiKeyRequest"}},"schema":{"$ref":"#\/components\/schemas\/RemoveApiKeyRequest"}}}}}},"\/api\/auth\/api-key\/generate":{"get":{"description":"Generates, stores, and returns a new API key with the default expiration period for the user currently logged in.\n\nOnce created, you can then provide this API key in an\n`Authorization: PL-API-KEY <Key>` header in replacement of an `X-PL-AUTH` header.","responses":{"200":{"content":{"application\/json":{"examples":{"GenerateApiKeyResponse":{"$ref":"#\/components\/examples\/GenerateApiKeyResponse"}},"schema":{"$ref":"#\/components\/schemas\/GenerateApiKeyResponse"}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"getApiAuthApi-keyGenerate","parameters":[{"in":"query","schema":{"nullable":true,"format":"int64","type":"integer"},"description":"Number of seconds until the key expires.","required":false,"example":2678400,"name":"expireSeconds"},{"in":"query","schema":{"nullable":true,"type":"string"},"description":"Role of the new key.","required":false,"example":"ReadOnlyUser","name":"role"}],"summary":"Generates an API key for the current user. Deprecated in favor of POST \/api\/auth\/api-keys.","tags":["Authentication"],"deprecated":true}},"\/api\/site\/copy":{"post":{"description":"Copies Project from one organization to a target organization given a project Identifier.\nAll UUID's are created new for the copied project.\nReturns a mapping of old identifiers and new identifiers of the given project.\nHistory data is NOT copied unless startDate and endDate are provided.\nHistory copy will take time to complete in the background and there will be no indication of success.","responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ObjectIdentifiers"}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiSiteCopy","parameters":[{"in":"query","name":"targetOrganizationID","schema":{"format":"uuid","type":"string"},"description":"Organization to copy the given site into.","required":true},{"in":"query","name":"siteID","schema":{"type":"string","format":"uuid"},"description":"Site to copy.","required":true},{"in":"query","name":"siteName","schema":{"type":"string","nullable":true},"description":"Optionally change the copied site name.","required":false},{"in":"query","name":"siteDirectory","schema":{"nullable":true,"type":"string"},"description":"Optionally change the copied site directory.","required":false},{"in":"query","name":"startDate","schema":{"type":"string","nullable":true},"description":"Date to start copying history from. Required to export all history.","required":false},{"in":"query","name":"endDate","schema":{"nullable":true,"type":"string"},"description":"Date to end copying history at. Required to export all history.","required":false}],"tags":["Site"],"summary":"Copy Project from one organization to another"}},"\/api\/image\/link":{"post":{"description":"Associates an existing `Image` node with an already-uploaded image file without re-uploading the file data. The user must have write access to the target image, and linking images across AuthGroups is not allowed.","responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ImageUploadResponse"},"examples":{"ImageUploadResponse":{"$ref":"#\/components\/examples\/ImageUploadResponse"}}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiImageLink","tags":["Images"],"summary":"Link an image node to an existing image file.","requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/LinkImageData"},"examples":{"LinkImageData":{"$ref":"#\/components\/examples\/LinkImageData"}}}}}}},"\/api\/graphqlSubscribe":{"get":{"tags":["GraphQL"],"description":"Serves the GraphQL API over websockets to support streaming results like subscriptions.\n\nThe following sub-protocols are supported:\n- [graphql-transport-ws](https:\/\/github.com\/enisdenjo\/graphql-ws\/blob\/master\/PROTOCOL.md)\n- [graphql-ws](https:\/\/github.com\/apollographql\/subscriptions-transport-ws\/blob\/master\/PROTOCOL.md)","operationId":"getApiGraphqlSubscribe","security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"summary":"GraphQL API over websocket."}},"\/api\/util\/version":{"get":{"operationId":"getApiUtilVersion","responses":{"200":{"description":"OK","content":{"text\/plain":{"schema":{"type":"string"},"examples":{"String":{"$ref":"#\/components\/examples\/String"}}}}}},"deprecated":true,"summary":"Returns version information","description":"Returns commit hashes & build times for the running build of the webserver and all apps.","tags":["Utility"]}},"\/api\/device\/login":{"get":{"description":"DEPRECATED: Device communication should use mTLS, where login is not required.\n\nLogs in a PassiveLogic device, responding with the authentication token.","responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/DeviceLoginResponse"},"examples":{"DeviceLoginResponse":{"$ref":"#\/components\/examples\/DeviceLoginResponse"}}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"getApiDeviceLogin","tags":["Device Communications"],"summary":"Logs in a PassiveLogic device","deprecated":true}},"\/api\/organization\/decline":{"post":{"description":"Rejects an invitation based on Organization invite ID.\nInvitation is deleted.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"},"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}}}}}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiOrganizationDecline","tags":["Organization"],"summary":"Declines an organization invitation","requestBody":{"content":{"application\/json":{"examples":{"DeclineOrganizationInvite":{"$ref":"#\/components\/examples\/DeclineOrganizationInvite"}},"schema":{"$ref":"#\/components\/schemas\/DeclineOrganizationInvite"}}},"required":true}}},"\/api\/graphql\/schema":{"get":{"description":"Returns the GraphQL schema served by this webserver in SDL (Schema Definition Language) format","externalDocs":{"url":"https:\/\/graphql.org\/learn\/schema\/","description":"GraphQL schema documentation."},"responses":{"200":{"description":"OK","content":{"text\/plain":{"schema":{"type":"string"},"examples":{"String":{"$ref":"#\/components\/examples\/String"}}}}}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"getApiGraphqlSchema","tags":["GraphQL"],"summary":"GraphQL schema"}},"\/api\/organization\/update-logo":{"post":{"description":"Verifies that the user making the request has sufficient permissions to change the logo for the organization.\nTakes in a new image as the organization's logo. Creates or replaces the previous stored logo.","responses":{"200":{"content":{"application\/json":{"examples":{"ImageUploadResponse":{"$ref":"#\/components\/examples\/ImageUploadResponse"}},"schema":{"$ref":"#\/components\/schemas\/ImageUploadResponse"}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiOrganizationUpdate-logo","tags":["Organization"],"summary":"Updates the organization's logo","requestBody":{"required":true,"content":{"application\/json":{"examples":{"OrganizationImageData":{"$ref":"#\/components\/examples\/OrganizationImageData"}},"schema":{"$ref":"#\/components\/schemas\/OrganizationImageData"}}}}}},"\/api\/image\/user-avatar":{"post":{"description":"Uploads and updates the avatar image URL with the new image URL.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ImageUploadResponse"},"examples":{"ImageUploadResponse":{"$ref":"#\/components\/examples\/ImageUploadResponse"}}}}}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiImageUser-avatar","tags":["Images"],"summary":"Update user avatar image","requestBody":{"content":{"application\/json":{"examples":{"ImageData":{"$ref":"#\/components\/examples\/ImageData"}},"schema":{"$ref":"#\/components\/schemas\/ImageData"}}},"required":true}}},"\/api\/device\/metadata":{"put":{"tags":["Device Communications"],"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"description":"Allows devices to communicate relevant information to the cloud.","operationId":"putApiDeviceMetadata","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/DeviceLoginResponse"},"examples":{"DeviceLoginResponse":{"$ref":"#\/components\/examples\/DeviceLoginResponse"}}}}}},"summary":"Accepts PassiveLogic device metadata"}},"\/api\/binding":{"post":{"description":"Uploads a file to the server and associates that file with a binding node, setting its new stored URL & checksum in the\ndatabase. The data may be sent in either multipart\/form-data or JSON format. If using JSON, the image file must be\nBase64-encoded. Using multipart\/form-data is recommended as it is a more efficient way to transmit binary data.","responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/BindingUploadResponse"},"examples":{"BindingUploadResponse":{"$ref":"#\/components\/examples\/BindingUploadResponse"}}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiBinding","tags":["Bindings"],"summary":"Uploads a file associated with a binding.","requestBody":{"required":true,"content":{"application\/json":{"examples":{"UploadFileData":{"$ref":"#\/components\/examples\/UploadFileData"}},"schema":{"$ref":"#\/components\/schemas\/UploadFileData"}},"multipart\/form-data":{"examples":{"UploadFileData":{"$ref":"#\/components\/examples\/UploadFileData"}},"schema":{"$ref":"#\/components\/schemas\/UploadFileData"}}}}}},"\/api\/authgroup\/{authGroupID}\/registeredDevices":{"get":{"description":"The results are ordered by serial number","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"items":{"$ref":"#\/components\/schemas\/RegisteredDevice"},"type":"array"}}}}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"getApiAuthgroupByAuthGroupIDRegisteredDevices","tags":["Auth Groups"],"parameters":[{"schema":{"type":"string"},"in":"path","required":true,"name":"authGroupID"}],"summary":"Returns the devices registered to this AuthGroup"}},"\/api\/device\/deltasync":{"get":{"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"getApiDeviceDeltasync","tags":["Delta Sync"],"description":"# Delta Sync:\n\nA protocol that keeps a chosen set of fields in sync between a device and the cloud. Only type names, IDs, and plain typed values cross the wire, never Quantum schema encoded objects, so it keeps working across any Quantum version combination, both directions, reactively.\n\nAuth is the device's client certificate (mTLS), and the auth group on the cert bounds what the cloud pushes down and what writes it accepts. No Quantum schema version check here.\n\nOn connect the device sends `reconcile` with the values that changed since its watermark and holds its live updates until the cloud confirms with the reconcile's `ack`. After that, both sides stream changes as they happen. A bad node or message never takes down the connection. A node the cloud can't apply comes back with its id and what went wrong. A field it can't read or doesn't recognize is logged and skipped, and the rest of the update still applies. A message it can't read at all comes back as an error saying why. The connection stays open through all of it.\n\n## Supported types and fields:\n\nRight now Delta Sync only carries:\n\n### `property`\n- `currentValue` (number)\n- `description` (text)\n\n## Delta Sync messageTypes:\n\n### `deltaUpdate`: One or more changed fields on an object, batched so everything from one debounce window travels together. Device to cloud it carries a `requestID` and the cloud answers it with an `ack`, so a batch that fails can be retried. Cloud to device it is a fire and forget push with no ack. A field with a value is set, a field sent as null is cleared, and a field left out is unchanged.\nex)\n```json\n{\n    \"messageType\": \"deltaUpdate\",\n    \"deviceID\": \"<serialNumber>\",\n    \"requestID\": \"<uuid>\",\n    \"payload\": { \"updates\": [\n        { \"property\": { \"id\": \"<uuid>\", \"currentValue\": 72.5 } },\n        { \"property\": { \"id\": \"<uuid>\", \"description\": \"Supply fan\" } },\n        { \"property\": { \"id\": \"<uuid>\", \"currentValue\": 68.0, \"description\": \"Zone 1 setpoint\" } },\n        { \"property\": { \"id\": \"<uuid>\", \"currentValue\": null } },\n        { \"property\": { \"id\": \"<uuid>\", \"description\": null } }\n    ] }\n}\n```\n\n### `reconcile`: Device to cloud, sent on connect. The values that changed since the device's watermark, or every synced field on the first connect. The device is the source of truth, so this is a push not a request. Carries a `requestID` and is answered by an `ack`. Same payload shape as `deltaUpdate`.\nex)\n```json\n{\n    \"messageType\": \"reconcile\",\n    \"deviceID\": \"<serialNumber>\",\n    \"requestID\": \"<uuid>\",\n    \"payload\": {\n        \"since\": \"2026-09-04T12:00:00Z\",\n        \"updates\": [\n            { \"property\": { \"id\": \"<uuid>\", \"currentValue\": 72.5, \"description\": \"Supply fan\" } }\n        ]\n    }\n}\n```\n\n### `ack`: Cloud to device. Answers a `reconcile` or a device-sent `deltaUpdate`, echoing its `requestID`. `errors` lists the objects the cloud could not apply, each with a typed `code`. An empty list means everything applied. For a `reconcile` it also lists any property the cloud itself changed since the device's watermark, which the device then sends up, so a reconcile is two sends when the cloud has drifted and one send plus this ack when it has not. Streaming starts after the reconcile's ack.\nex)\n```json\n{\n    \"messageType\": \"ack\",\n    \"deviceID\": \"<serialNumber>\",\n    \"requestID\": \"<uuid>\",\n    \"payload\": {\n        \"errors\": [\n            { \"id\": \"<uuid>\", \"code\": \"notFound\", \"message\": \"Property not present in the cloud.\" }\n        ],\n        \"changed\": [\"<uuid>\"]\n    }\n}\n```\n\n### `error`: Either direction. Something was rejected, e.g. bad auth or an unknown message.\nex)\n```json\n{\n    \"messageType\": \"error\",\n    \"deviceID\": \"<serialNumber>\",\n    \"payload\": { \"code\": \"unauthorized\", \"message\": \"...\" }\n}\n```","summary":"Delta Sync API over websockets"}},"\/api\/authgroup\/join":{"post":{"description":"Used by the front end `\/app\/login\/post-auth` route to accept project (auth group) invitations. The role the user receives in the\nreferenced AuthGroup is determined through AuthGroup invite role; if no role is defined, we default to read-only user.","responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"},"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiAuthgroupJoin","tags":["Auth Groups"],"summary":"Accepts an AuthGroup Invitation","deprecated":true,"requestBody":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/AcceptOrganizationInvite"},"examples":{"AcceptOrganizationInvite":{"$ref":"#\/components\/examples\/AcceptOrganizationInvite"}}}},"required":true}},"get":{"description":"DEPRECATED: GET \/api\/authgroup\/join was used to accept a project (auth group) invitation by clicking directly in an email.\nAccepting invitations is now handled by the \/app\/login\/post-auth route provided by the front end, which calls POST\n\/api\/authgroup\/join. This can be removed when any invitations linked to in this way have expired.","responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"},"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"getApiAuthgroupJoin","tags":["Auth Groups"],"summary":"Accepts an AuthGroup Invitation","deprecated":true,"requestBody":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/AcceptOrganizationInvite"},"examples":{"AcceptOrganizationInvite":{"$ref":"#\/components\/examples\/AcceptOrganizationInvite"}}}},"required":true}}},"\/api\/auth\/password\/reset\/change":{"post":{"description":"DEPRECATED: This endpoint is not available when Keycloak authentication is turned on and will be removed in the future.\n\nChanges a user's password to the specified value, reached from password reset flow.\n\nToken authentication required, only using the authentication JWT decoded from the password reset URL sent to a user\nfrom a reset initiation.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenericMessageResponse"},"examples":{"GenericMessageResponse":{"$ref":"#\/components\/examples\/GenericMessageResponse"}}}}}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiAuthPasswordResetChange","tags":["Authentication"],"summary":"Changes a user's password from the password reset flow.","deprecated":true,"requestBody":{"content":{"application\/json":{"examples":{"ChangePasswordData":{"$ref":"#\/components\/examples\/ChangePasswordData"}},"schema":{"$ref":"#\/components\/schemas\/ChangePasswordData"}}},"required":true}}},"\/api\/auth\/register":{"post":{"description":"DEPRECATED: This endpoint is not available when Keycloak authentication is turned on and will be removed in the future.\n\nCreates a new user based on the provided information. Requires jwt obtained via email from\n`\/api\/auth\/register\/initiate`. The user status is set to `Active` and immediately logged in.\n\n\n\nRegistration token is required when email verification is enforced, as indicated by a failed response to this endpoint","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/RegisterUserResponse"},"examples":{"RegisterUserResponse":{"$ref":"#\/components\/examples\/RegisterUserResponse"}}}}}},"operationId":"postApiAuthRegister","tags":["Authentication"],"summary":"Registers a new user","deprecated":true,"requestBody":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/RegisterUserData"},"examples":{"RegisterUserData":{"$ref":"#\/components\/examples\/RegisterUserData"}}}},"required":true}}},"\/api\/image":{"post":{"description":"Uploads an image to the server, setting its new stored url & checksum in the database. If an existing ID is given, this will\nattempt an update of the image data. The data may be sent in either multipart\/form-data or JSON format. If using JSON, the\nimage file must be Base64-encoded. Using multipart\/form-data is recommended as it is a more efficient way to transmit binary\ndata.\n\nThis endpoint is intended for associating image file data with an existing `Image` node in the Quantum schema.","responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ImageUploadResponse"},"examples":{"ImageUploadResponse":{"$ref":"#\/components\/examples\/ImageUploadResponse"}}}},"description":"OK"}},"security":[{"Authentication Token":[]},{"DEPRECATED - API Key":[]},{"API Key":[]},{"Basic Auth":[]}],"operationId":"postApiImage","tags":["Images"],"summary":"Uploads an image.","requestBody":{"content":{"multipart\/form-data":{"examples":{"UploadImageData":{"$ref":"#\/components\/examples\/UploadImageData"}},"schema":{"$ref":"#\/components\/schemas\/UploadImageData"}},"application\/json":{"examples":{"UploadImageData":{"$ref":"#\/components\/examples\/UploadImageData"}},"schema":{"$ref":"#\/components\/schemas\/UploadImageData"}}},"required":true}}}},"components":{"securitySchemes":{"Authentication Token":{"description":"A user's authentication token acquired from the login process","type":"apiKey","in":"header","name":"X-PL-AUTH"},"DEPRECATED - API Key":{"description":"DEPRECATED - A user's API key, provided as an `Authorization` scheme","type":"apiKey","in":"header","name":"Authorization: PL-API-KEY"},"API Key":{"description":"A user's API key","type":"apiKey","in":"header","name":"PL-API-KEY"},"Basic Auth":{"description":"Basic authentication","type":"http","scheme":"basic"}},"examples":{"GenericMessageResponse":{"value":{"message":"We will watch your career with great interest."}},"InviteUserToAuthGroupData":{"value":{"authGroupID":"6C8829E7-5E1F-4E7D-B0D9-A98394AAC760","role":"StandardUser","inviteEmail":"mithrawnuruodo@galactic-empire.com","expiration":"2027-10-20T17:34:41Z"}},"LinkImageData":{"value":{"sourceID":"922CC011-24D9-44FD-B099-DDBF7430CFBC","targetID":"89287D71-A416-48FA-9581-CED16213C13A"}},"RemoveApiKeyRequest":{"value":{"key":"564d9054fb2cf06e3592c8c4fdc1f613822b0de6358d8580a78ae0b204be3f95"}},"RequestResetPasswordData":{"value":{"usernameOrEmail":"darthvader@galactic-empire.com"}},"LoginResponse":{"value":{"whoAmI":{"lastName":"Skywalker","emailVerified":true,"userID":"44AA4938-DD7F-4FAB-A42F-153AAF906C66","eulaAccepted":true,"firstName":"Anakin"},"refresh":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJQYXNzaXZlTG9naWMiLCJzdWIiOiJBUEkiLCJleHAiOjE1MTYyMzkwMjIsIklEIjoiMjAyZTI0MTEtNjQ5OS00YWJlLTk5MmYtMmZmODU1MWY5NmU5IiwidXNlcklEIjoiMzMwYTFmMjktMTlkMC00Y2M5LWI3MjQtOWJmNDQxNWY4ODY1In0.hyyBxcHBXqYzwZehxqW7aCwfQTlngtCc7gRW31ijK50","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJQYXNzaXZlTG9naWMiLCJzdWIiOiJBUEkiLCJleHAiOjE1MTYyMzkwMjIsIklEIjoiMjAyZTI0MTEtNjQ5OS00YWJlLTk5MmYtMmZmODU1MWY5NmU5IiwibmFtZSI6ImRhcnRodmFkZXIiLCJzdGF0dXMiOiJBY3RpdmUifQ.KiI6-jHPAMsDHvyA7U-BaRZ8mZnByF9a1887lVb-1lY"}},"UpdatedUserEmail":{"value":{"newEmail":"emperorpalps@gallactic-empire.com","oldEmail":"sheevpalpatine@old-republic.net","password":"unlimitedP0wuh!!!"}},"UploadImageData":{"value":{"fileType":"svg","ID":"4A8CD1EF-AE96-49FC-BAAA-FBA9040A3DC2","image":"PHN2ZyBoZWlnaHQ9IjI0IiB3aWR0aD0iMjQiPjxjaXJjbGUgY3g9IjUwIiBjeT0iNTAiIHI9IjQwIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjMiIGZpbGw9ImJsdWUiIC8+PC9zdmc+"}},"ChangePasswordData":{"value":{"newPassword":"SandSuxDest0ryAllSand"}},"AuthGroupRemovalResponse":{"value":{"message":"User has been successfully removed!","authGroupID":"7CFCA52E-B11A-4A99-B2F2-4A91B8F0F8E8","userID":"897B9392-7762-433E-8F91-60F165FEDA12"}},"CreatedApiKeyResponse":{"value":{"apiKey":{"expiration":"2026-11-20T17:34:42Z","name":"CI deployment","id":"EB2185F6-E2A8-4DAD-9382-8432E091CB57","role":"read"},"key":"564d9054fb2cf06e3592c8c4fdc1f613822b0de6358d8580a78ae0b204be3f95"}},"ArrayApiKeyResponse":{"value":[{"role":"read","id":"EB2185F6-E2A8-4DAD-9382-8432E091CB57","expiration":"2026-11-20T17:34:42Z","name":"CI deployment"}]},"UpdateApiKeyRequest":{"value":{"name":"Updated CI deployment","role":"read"}},"InviteUserToOrganizationData":{"value":{"expiration":"2027-10-20T17:34:42Z","inviteEmail":"mithrawnuruodo@galactic-empire.com","orgID":"341ACC14-DE6E-49D8-877E-C7A6FA95F305","role":"StandardUser"}},"OrgInviteResponse":{"value":{"message":"Invitation sent.","inviteID":"C54B8A9B-67F0-4D95-86F1-87F9B1F30AE2"}},"DeleteOrganizationData":{"value":{"orgID":"004AF47B-7697-4A4E-BEEA-2A3D3C50BC96"}},"OrganizationImageData":{"value":{"organizationID":"3FF70B83-99C0-4F10-93F0-D7618D295B4E","image":"PHN2ZyBoZWlnaHQ9IjI0IiB3aWR0aD0iMjQiPjxjaXJjbGUgY3g9IjUwIiBjeT0iNTAiIHI9IjQwIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjMiIGZpbGw9ImJsdWUiIC8+PC9zdmc+","fileType":"svg"}},"DeviceLoginResponse":{"value":{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCIsImtpZCI6InBsLUUwOTk2NzlGLTQyNDQtNDA4Ni05NTZBLTg4OUM4QkVCQzg2QSJ9.eyJpc3N1ZXIiOiJQYXNzaXZlTG9naWMiLCJoaXZlSUQiOiJEMjU3M0M2Qy0wQjVBLTRCMzItQjM1Ni00OEEyOTlFRDE0OTMiLCJleHBpcmF0aW9uIjoxNzE0NDA2OTA3LjcyODE1Mywic3ViamVjdCI6IkhpdmUgQXV0aGVudGljYXRpb24iLCJoaXZlU2VyaWFsTnVtYmVyIjoiUTBSWFQyUE1HWCJ9.11Awfpm7Uo1LWBtZTCNn1pxUuliE3lcMF4bIqbJzX87u5zJRFRKqfNWdULlU7mmEq_Fes7CzEvSFmn7wlNLlZxY-T8NKhFq35raskqDW-ZdpXUKRk8aHyWfrCVeFcCZU"}},"ImageData":{"value":{"image":"PHN2ZyBoZWlnaHQ9IjI0IiB3aWR0aD0iMjQiPjxjaXJjbGUgY3g9IjUwIiBjeT0iNTAiIHI9IjQwIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjMiIGZpbGw9ImJsdWUiIC8+PC9zdmc+","fileType":"svg"}},"String":{"value":"some string"},"GenerateApiKeyResponse":{"value":{"key":"564d9054fb2cf06e3592c8c4fdc1f613822b0de6358d8580a78ae0b204be3f95","expiration":845832881936082}},"RegisterUserResponse":{"value":{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJQYXNzaXZlTG9naWMiLCJzdWIiOiJBUEkiLCJleHAiOjE1MTYyMzkwMjIsIklEIjoiMjAyZTI0MTEtNjQ5OS00YWJlLTk5MmYtMmZmODU1MWY5NmU5IiwibmFtZSI6ImRhcnRodmFkZXIiLCJzdGF0dXMiOiJBY3RpdmUifQ.KiI6-jHPAMsDHvyA7U-BaRZ8mZnByF9a1887lVb-1lY","message":"User registered successfully.","refresh":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJQYXNzaXZlTG9naWMiLCJzdWIiOiJBUEkiLCJleHAiOjE1MTYyMzkwMjIsIklEIjoiMjAyZTI0MTEtNjQ5OS00YWJlLTk5MmYtMmZmODU1MWY5NmU5IiwidXNlcklEIjoiMzMwYTFmMjktMTlkMC00Y2M5LWI3MjQtOWJmNDQxNWY4ODY1In0.hyyBxcHBXqYzwZehxqW7aCwfQTlngtCc7gRW31ijK50","userStatus":"Active","newUserID":{"id":"4EA9FB0F-6C13-4745-8AED-BEA4260B5F40","type":"User"}}},"BindingUploadResponse":{"value":{"message":"Binding object was successfully updated.","url":"https:\/\/passivelogic.com\/binding\/7D9A7B35-31AD-4FE6-93DB-4428D9FF3A3B"}},"WhoAmIResponse":{"value":{"firstName":"Anakin","eulaAccepted":true,"emailVerified":true,"userID":"694DE1A6-01C9-43B7-8470-C15BFCD371E4","lastName":"Skywalker"}},"UploadFileData":{"value":{"ID":"AB4DE61A-E1E0-4A86-BD29-9CB68A79C15F","fileType":"svg","file":"PHN2ZyBoZWlnaHQ9IjI0IiB3aWR0aD0iMjQiPjxjaXJjbGUgY3g9IjUwIiBjeT0iNTAiIHI9IjQwIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjMiIGZpbGw9ImJsdWUiIC8+PC9zdmc+"}},"AuthGroupInviteResponse":{"value":{"message":"Invitation sent.","inviteID":"5C9BAF2C-BC3B-4AE1-BD7E-53EC9809BEF5"}},"AcceptOrganizationInvite":{"value":{"inviteID":"B6ED5C11-12F4-455B-B36D-25E04F0DEB1B"}},"LinkFileData":{"value":{"targetID":"4458BB43-37E4-4E62-AD87-B7F6EA648166","sourceID":"8BAE480B-1EEE-4CEB-A579-586EBCA5BE2B"}},"RegisterUserData":{"value":{"acceptedEULA":true,"token":"<JWT Token from registration email>","acceptMailingList":false,"password":"Ih8obi-wan!higrndSUXX","email":"darthvader@galactic-empire.com","username":"94DA3152-C2C1-4534-865F-D8926D5D6625","lastName":"Skywalker","firstName":"Anakin"}},"DeclineOrganizationInvite":{"value":{"inviteID":"055CB192-D462-4CE9-9EAB-229276CABD2E"}},"CreateApiKeyRequest":{"value":{"role":"read","name":"CI deployment","expiration":"2026-11-20T17:34:42Z"}},"ApiKeyResponse":{"value":{"name":"CI deployment","expiration":"2026-11-20T17:34:42Z","role":"read","id":"EB2185F6-E2A8-4DAD-9382-8432E091CB57"}},"DeviceRegistrationResponse":{"value":{"authGroupID":"B6A24452-BCB8-45B4-A70C-5A6491A5343A","deviceSerialNumber":"ABCDEF12346"}},"ImageUploadResponse":{"value":{"url":"http:\/\/www.passivelogic.com\/image\/85073765-4DB9-4DC0-B911-8DF892C6CAFB","message":"Image was succefully updated."}},"UserRegistrationInitiation":{"value":{"email":"darthvader@galactic-empire.com"}}},"schemas":{"ImageData":{"required":["fileType","image"],"properties":{"fileType":{"description":"The type of file the given data represents, from a list of allowed files.","type":"string"},"image":{"description":"File data to write to the server. Must be Base64-encoded if sending as JSON.","type":"string","format":"byte"}},"type":"object","description":"Data required to upload image without using an image model."},"ComfortControllerComfortAdjustmentRequest":{"required":["adjustmentLevel","zoneGroupID"],"properties":{"adjustmentLevel":{"description":"An integer between -2 and 2 that indicates how the comfort should be adjusted. Values less than zero indicate that the space should become cooler, while values above zero indicate that the space should become hotter.","format":"int64","type":"integer"},"zoneGroupID":{"description":"The ID of the zone group to adjust comfort for.","format":"uuid","type":"string"}},"type":"object","description":"Comfort adjustment inputs."},"InviteUserToOrganizationData":{"required":["inviteEmail","orgID"],"properties":{"inviteEmail":{"type":"string","description":"Email to send the invite to"},"orgID":{"format":"uuid","type":"string","description":"ID of the organization to which this invite pertains"},"expiration":{"nullable":true,"format":"date-time","type":"string","description":"Timestamp at which this invitation will no longer be valid"},"role":{"$ref":"#\/components\/schemas\/QuantumRole"}},"type":"object","description":"Parameters required to invite a user to an organization"},"LinkImageData":{"required":["sourceID","targetID"],"properties":{"sourceID":{"description":"UUID of a quantum image node already connected to the desired image file.","type":"string","format":"uuid"},"targetID":{"description":"UUID of the quantum image node to update.","type":"string","format":"uuid"}},"type":"object","description":"Data required to link an image node to an existing image file."},"ApiKeyResponse":{"required":["expiration","id","role"],"properties":{"name":{"description":"Optional API key label.","type":"string","nullable":true},"id":{"description":"Unique API key ID.","type":"string","format":"uuid"},"role":{"description":"Role assigned to the API key. Options are `read` or `readWrite`.","type":"string"},"expiration":{"description":"Expiration time.","type":"string","format":"date-time"}},"type":"object","description":"API key metadata. The secret is never included in this representation."},"UploadFileData":{"required":["ID","file","fileType"],"properties":{"file":{"type":"string","description":"File data to write to the server.","format":"byte"},"ID":{"type":"string","description":"UUID of the quantum binding node that you want to associate this file with.","format":"uuid"},"fileType":{"type":"string","description":"The type of file the given data represents."}},"type":"object","description":"Data required to upload a file binding."},"RegisteredDevice":{"type":"object"},"UpdateApiKeyRequest":{"properties":{"role":{"type":"string","description":"Updated role. Roles may only be reduced. Options are `read` or `readWrite`.","nullable":true},"name":{"type":"string","description":"Updated label for the API key.","nullable":true}},"type":"object","description":"Mutable API key metadata. At least one field must be provided."},"UserRegistrationInitiation":{"type":"object","required":["email"],"description":"Information for intiating user registration","properties":{"firstName":{"type":"string","nullable":true},"email":{"type":"string","description":"Email that is registering for a new account."},"lastName":{"type":"string","nullable":true}}},"CreatedApiKeyResponse":{"required":["apiKey","key"],"properties":{"key":{"description":"API key secret. This value is returned only at creation.","type":"string"},"apiKey":{"$ref":"#\/components\/schemas\/ApiKeyResponse"}},"type":"object","description":"A newly created API key and its one-time secret."},"TailscalePostTailnetKeyResponse":{"required":["capabilities","expirySeconds","id","key","keyType"],"properties":{"expires":{"nullable":true,"format":"date-time","type":"string"},"expirySeconds":{"format":"int64","type":"integer"},"tags":{"type":"array","nullable":true,"items":{"type":"string"}},"capabilities":{"$ref":"#\/components\/schemas\/TailscaleCapabilities"},"userId":{"type":"string","nullable":true},"created":{"nullable":true,"format":"date-time","type":"string"},"scopes":{"type":"array","nullable":true,"items":{"type":"string"}},"invalid":{"type":"boolean","nullable":true},"keyType":{"type":"string"},"id":{"type":"string"},"revoked":{"nullable":true,"format":"date-time","type":"string"},"key":{"type":"string"},"description":{"type":"string","nullable":true}},"type":"object"},"DeployedOnHiveResponse":{"required":["runningOnHive"],"properties":{"runningOnHive":{"type":"boolean"}},"type":"object"},"AuthGroupRemovalResponse":{"required":["authGroupID","message","userID"],"properties":{"message":{"type":"string","description":"Message with additional information"},"authGroupID":{"type":"string","description":"ID of the Authgroup the user has been removed from.","format":"uuid"},"userID":{"type":"string","description":"ID of the User who has been removed","format":"uuid"}},"type":"object","description":"Response indicating that a User has been removed from an AuthGroup."},"UpdatedUserEmail":{"required":["newEmail","oldEmail","password"],"properties":{"oldEmail":{"description":"Previous user email","type":"string"},"newEmail":{"description":"New user email","type":"string"},"password":{"description":"user's password to confirm identity","type":"string"}},"type":"object","description":"Information for changing a user's email"},"TailscaleCapabilitiesDevices":{"required":["create"],"properties":{"create":{"$ref":"#\/components\/schemas\/TailscaleCapabilitiesDevicesCreate"}},"type":"object"},"InviteUserToAuthGroupData":{"required":["authGroupID","inviteEmail"],"properties":{"inviteEmail":{"description":"Email to send the invite to","type":"string"},"expiration":{"nullable":true,"description":"Timestamp at which this invitation will no longer be valid","type":"string","format":"date-time"},"authGroupID":{"description":"ID of the AuthGroup to which this invite pertains","type":"string","format":"uuid"},"role":{"$ref":"#\/components\/schemas\/QuantumRole"}},"type":"object","description":"Parameters required to invite a user to an organization"},"QuantumRole":{"enum":["PrivilegedUser","ReadOnlyUser","StandardUser","SuperUser"],"type":"string"},"DeclineOrganizationInvite":{"required":["inviteID"],"properties":{"inviteID":{"format":"uuid","type":"string","description":"ID of the invitation being declined."}},"type":"object","description":"Parameters required to decline an organization invite"},"QuantumUserStatus":{"enum":["Active","Blocked","Deleted","Locked","Pending","Reset"],"type":"string"},"ComfortControllerComfortAdjustmentResponseOccupant":{"required":["ID","activityLevelInMETs","clothingInsulation","predictedMeanVote","targetPredictedMeanVote"],"properties":{"ID":{"description":"The ID of the adjusted occupant.","format":"uuid","type":"string"},"activityLevelInMETs":{"description":"The adjusted occupant activity level.","format":"double","type":"number"},"temperatureDeltaC":{"nullable":true,"description":"The expected change in zone temperature in degrees Celsius, rounded to the nearest tenth. Positive is warmer and negative is cooler. Omitted when the solver cannot provide an estimate.","format":"double","type":"number"},"clothingInsulation":{"description":"The adjusted occupant clothing insulation.","format":"double","type":"number"},"predictedMeanVote":{"description":"The predicted occupant mean vote.","format":"double","type":"number"},"targetPredictedMeanVote":{"description":"The new target occupant mean vote.","format":"double","type":"number"}},"type":"object","description":"Comfort adjustment inputs."},"DeviceRegistrationResponse":{"required":["authGroupID","deviceSerialNumber"],"properties":{"deviceSerialNumber":{"type":"string"},"authGroupID":{"type":"string","format":"uuid"}},"type":"object"},"OrgInviteResponse":{"required":["inviteID","message"],"properties":{"message":{"type":"string","description":"Message with additional information"},"inviteID":{"format":"uuid","type":"string","description":"ID of the invitation generated\/sent"}},"type":"object","description":"Response indicating that an organization invitation was successfully sent."},"QuantumLensResponse":{"required":["status","user"],"properties":{"status":{"$ref":"#\/components\/schemas\/QuantumLensAppStatusResponse"},"user":{"$ref":"#\/components\/schemas\/QuantumLensUser"}},"type":"object"},"LinkFileData":{"required":["sourceID","targetID"],"properties":{"sourceID":{"description":"UUID of a quantum binding node already connected to the desired image file.","type":"string","format":"uuid"},"targetID":{"description":"UUID of the quantum binding node to update.","type":"string","format":"uuid"}},"type":"object","description":"Data required to link a binding node to an existing binding file."},"UploadImageData":{"required":["ID","fileType","image"],"properties":{"image":{"type":"string","description":"File data to write to the server. Must be Base64-encoded if sending as JSON.","format":"byte"},"ID":{"type":"string","description":"UUID of the quantum image node that you want to associate this image file with.","format":"uuid"},"fileType":{"type":"string","description":"The type of file the given data represents, from a list of allowed files."}},"type":"object","description":"Data required to upload an image."},"Identifier":{"required":["id","type"],"properties":{"type":{"$ref":"#\/components\/schemas\/QuantumObjectType"},"id":{"type":"string"}},"type":"object"},"ChangePasswordData":{"required":["newPassword"],"properties":{"newPassword":{"description":"New password for the user.","type":"string"},"confirmNewPassword":{"nullable":true,"type":"string"}},"type":"object","description":"Information required to change a user's password from the reset password flow"},"GraphQLRequest":{"required":["query"],"properties":{"variables":{"additionalProperties":{"type":"boolean"},"type":"object","nullable":true},"query":{"type":"string"},"operationName":{"type":"string","nullable":true}},"type":"object"},"QuantumLensAppStatusResponse":{"properties":{"osUpdate":{"nullable":true,"type":"boolean"},"appUpdate":{"nullable":true,"type":"boolean"}},"type":"object"},"AcceptOrganizationInvite":{"required":["inviteID"],"properties":{"inviteID":{"type":"string","description":"ID of the invitation being accepted.","format":"uuid"}},"type":"object","description":"Parameters required to accept an organization invite"},"LoginResponse":{"type":"object","required":["refresh","token","whoAmI"],"description":"Generic response information","properties":{"refresh":{"description":"Signed refresh JWT","type":"string"},"whoAmI":{"$ref":"#\/components\/schemas\/WhoAmIResponse"},"token":{"description":"Signed Authentication JWT","type":"string"}}},"QuantumObjectType":{"enum":["Adjacency","AllowedQuanta","Analysis","Animation","ApiKey","Artifact","Asset","AuthGroup","Behavior","Binding","Building","Campus","CampusDirectory","CompatibleQuanta","ConnectionNet","ConnectionNode","ConstructionCategory","ControlKnot","Device","DisplayRule","EmailVerification","Equation","Equipment","EquipmentComponent","EquipmentComponentPropertyRouter","EscalationGroup","Event","EventClass","EventHistory","EventRelation","EventType","Floor","GraphVersion","History","Image","ImportedModel","InterfaceAlternate","Invite","Layer","LayerSet","LayerSetJoin","Library","Location","LocationProperty","Manufacturer","Media","MediaComponent","Model","MultifactorAuthentication","NodeSet","NodeSetConnectionNodeJoin","Organization","OrganizationAuthGroupPermission","Prediction","PredictionGroup","Preference","PreferenceGroup","PrivilegeJoin","Procedure","Property","PropertyTagRouter","Quanta","RefreshToken","Relationship","SelectedModel","SessionBinding","SessionEvent","Shape","Site","Subsystem","SubsystemCapability","SubsystemSet","Surface","System","Tag","TagFamily","TypicalConstruction","UnitLookup","UnitPreference","User","UserSession","UserSessionHistory","Vertex","View","WeatherSource","Zone","ZoneGroup","ZoneSubsystem","ZoneSubsystemSet"],"type":"string"},"DeleteOrganizationData":{"required":["orgID"],"properties":{"orgID":{"type":"string","description":"ID of the organization being deleted","format":"uuid"}},"type":"object","description":"Parameters require to delete an organization"},"ImageUploadResponse":{"required":["message"],"properties":{"message":{"description":"Informative message relating to image upload. Updated image url with cache bust.","type":"string"},"url":{"type":"string","nullable":true}},"type":"object","description":"Image response definition"},"GenerateApiKeyResponse":{"required":["expiration","key"],"properties":{"expiration":{"type":"integer","description":"Expiration of the new key.","format":"int64"},"key":{"type":"string","description":"New API key."}},"type":"object","description":"New API key generated for a user."},"TailscaleCapabilities":{"required":["devices"],"properties":{"devices":{"$ref":"#\/components\/schemas\/TailscaleCapabilitiesDevices"}},"type":"object"},"TailscaleCapabilitiesDevicesCreate":{"required":["ephemeral","preauthorized","reusable","tags"],"properties":{"ephemeral":{"type":"boolean"},"preauthorized":{"type":"boolean"},"reusable":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}}},"type":"object"},"RegisterUserResponse":{"required":["message"],"properties":{"message":{"type":"string","description":"Additional information about the response"},"refresh":{"nullable":true,"type":"string","description":"New refresh JWT"},"newUserID":{"$ref":"#\/components\/schemas\/Identifier"},"token":{"nullable":true,"type":"string","description":"New authentication JWT containing changed information"},"userStatus":{"$ref":"#\/components\/schemas\/QuantumUserStatus"}},"type":"object","description":"Sent when a new user is successfully registered."},"QuantumLensUser":{"required":["userID"],"properties":{"firstName":{"nullable":true,"type":"string"},"lastName":{"nullable":true,"type":"string"},"userID":{"format":"uuid","type":"string"}},"type":"object"},"RemoveApiKeyRequest":{"required":["key"],"properties":{"key":{"type":"string","description":"API key to revoke."}},"type":"object","description":"API key to revoke"},"GraphQLResult":{"properties":{"errors":{"type":"array","nullable":true,"items":{"$ref":"#\/components\/schemas\/GraphQLError"}},"data":{"type":"boolean","nullable":true}},"type":"object"},"OrganizationImageData":{"required":["fileType","image","organizationID"],"properties":{"image":{"format":"byte","description":"File data to write to the server. Must be Base64-encoded if sending as JSON.","type":"string"},"fileType":{"description":"The type of file the given data represents, from a list of allowed files","type":"string"},"organizationID":{"format":"uuid","description":"The organization that is associated with this image request.","type":"string"}},"type":"object","description":"Data required to upload a organization image"},"DeviceRegistrationRequest":{"required":["siteID"],"properties":{"followerSerialNumber":{"type":"string","nullable":true},"userJWT":{"type":"string","nullable":true},"siteID":{"type":"string","format":"uuid"}},"type":"object"},"CreateApiKeyRequest":{"required":["expiration","name","role"],"properties":{"name":{"type":"string","description":"Label for the API key."},"expiration":{"format":"date-time","type":"string","description":"Expiration time."},"role":{"type":"string","description":"Role assigned to the API key. Options are `read` or `readWrite`."}},"type":"object","description":"Options for a new API key. The secret and owner are assigned by the server."},"RegisterUserData":{"required":["acceptMailingList","acceptedEULA","email","firstName","lastName","password"],"properties":{"password":{"type":"string","description":"User's password"},"acceptedEULA":{"type":"boolean","description":"Flag to mark if the user has accepted the EULA"},"lastName":{"type":"string","description":"User's last name"},"acceptMailingList":{"type":"boolean","description":"Flag to mark if the user wants marketing emails"},"firstName":{"type":"string","description":"User's first name"},"email":{"type":"string","description":"User's email"},"username":{"type":"string","nullable":true},"token":{"type":"string","nullable":true}},"type":"object","description":"Data required for user registration"},"GraphQLError":{"required":["locations","message","path"],"properties":{"message":{"type":"string"},"locations":{"items":{"$ref":"#\/components\/schemas\/SourceLocation"},"type":"array"},"path":{"items":{"items":{"type":"integer","format":"int64"},"type":"array"},"type":"array"},"extensions":{"nullable":true,"type":"object","additionalProperties":{"type":"boolean"}}},"type":"object"},"MagicLinkResponse":{"type":"object","description":"Email for use signing up with a magic link","properties":{"message":{"nullable":true,"description":"Message describing the response type.","type":"string"},"url":{"nullable":true,"description":"Url to be used a magic link login.","type":"string"}}},"GenericMessageResponse":{"type":"object","required":["message"],"description":"Generic response information","properties":{"message":{"description":"Informative message relating to the specific response","type":"string"}}},"JWKS":{"type":"object","required":["keys"],"properties":{"keys":{"type":"array","items":{"$ref":"#\/components\/schemas\/JWK"}}}},"BindingUploadResponse":{"required":["message"],"properties":{"message":{"type":"string","description":"Informative message relating to binding upload. Updated binding url with cache bust."},"url":{"type":"string","nullable":true}},"type":"object","description":"Binding response definition"},"WhoAmIResponse":{"type":"object","required":["userID"],"description":"Information about the authenticated user","properties":{"eulaAccepted":{"nullable":true,"type":"boolean","description":"Flag to mark if the user has acept the terms of the EULA"},"firstName":{"nullable":true,"type":"string","description":"User's first name"},"userID":{"format":"uuid","type":"string","description":"Unique ID for the user"},"lastName":{"nullable":true,"type":"string","description":"User's last name"},"emailVerified":{"nullable":true,"type":"boolean","description":"Flag to mark if the user's email is verified"}}},"ComfortControllerComfortAdjustmentResponse":{"required":["occupants","zoneGroupID"],"properties":{"zoneGroupID":{"format":"uuid","type":"string","description":"The ID of the zone group that comfort was adjusted for."},"occupants":{"items":{"$ref":"#\/components\/schemas\/ComfortControllerComfortAdjustmentResponseOccupant"},"type":"array","description":"The adjusted occupant settings"}},"type":"object","description":"Comfort adjustment inputs."},"SourceLocation":{"required":["column","line"],"properties":{"column":{"type":"integer","format":"int64"},"line":{"type":"integer","format":"int64"}},"type":"object"},"RequestResetPasswordData":{"required":["usernameOrEmail"],"properties":{"usernameOrEmail":{"type":"string","description":"Email of the user requesting a reset."}},"type":"object","description":"Information required to request a password reset."},"ObjectPair":{"required":["newID","oldID","type"],"properties":{"newID":{"type":"string","format":"uuid"},"oldID":{"type":"string","format":"uuid"},"type":{"$ref":"#\/components\/schemas\/QuantumObjectType"}},"type":"object"},"AuthGroupInviteResponse":{"required":["inviteID","message"],"properties":{"message":{"description":"Message with additional information","type":"string"},"inviteID":{"format":"uuid","description":"ID of the invitation generated\/sent","type":"string"}},"type":"object","description":"Response indicating that an AuthGroup invitation was successfully sent."},"JWK":{"type":"object","required":["kty"],"properties":{"kty":{"type":"string"}}},"DeviceLoginResponse":{"required":["token"],"properties":{"token":{"type":"string","description":"Signed authentication JWT"}},"type":"object","description":"Response to \/api\/device\/login"},"ObjectIdentifiers":{"required":["objectIdentifierMap"],"properties":{"objectIdentifierMap":{"type":"array","items":{"$ref":"#\/components\/schemas\/ObjectPair"}}},"type":"object"}}},"info":{"title":"PassiveLogic HTTP API","description":"Welcome to the PassiveLogic HTTP API documentation!\n\nThis API is currently considered **beta**. While we prioritize stability, breaking changes may be occasionally introduced. Breakages will be deprecated prior to removal, and deprecated endpoints will include the `X-PL-DEPRECATED` header in the response. It's recommended that any integrations warn the developer when this header is present.\n\n**Important**: Making changes with the API can cause problems with user applications that might not be reversible, or might be fixable only with PassiveLogic support.\n\n## Getting Started\n\nIf you haven't already, register your user at [https:\/\/passivelogic.com\/app\/login\/](https:\/\/passivelogic.com\/app\/login\/) by clicking \"Sign Up\" and following the instructions.\n\n### Using an Authorization Token\n\nWhen a user logs in through an application, they are provided a short-lived authentication token and a long-lived refresh token. The authentication token can be used to authenticate API requests by including it in the `X-PL-AUTH` header. Upon expiration of the authentication token, the refresh token may be used to create a new authentication token.\n\n### Using an API Key\n\nUsers can manage API keys using the `\/api\/auth\/api-keys` REST resource. Creation returns the secret once; subsequent reads expose metadata only. API keys authenticate requests through the `PL-API-KEY` header. The singular API-key lifecycle routes and GraphQL API-key reads are deprecated.\n\n## Device Tunnel\n\nHTTP requests can be tunneled directly to devices by appending their path onto `\/api\/tunnel\/<serialNumber>\/`. Example: `https:\/\/passivelogic.com\/api\/tunnel\/SN123\/api\/graphql`. Cloud authentication and authorization is respected by the device. Request and response formats match the device server's formats.","version":"0.0.0"},"tags":[{"name":"Authentication","description":"Routes related to user auth"},{"name":"Auth Groups","description":"Routes related to AuthGroup management."},{"name":"Bindings","description":"Routes related to Binding management."},{"name":"Comfort","description":"Routes related to comfort controls"},{"name":"Export","description":"Routes related to exporting objects and history"},{"name":"GraphQL","description":"Routes related to the GraphQL API"},{"name":"Images","description":"Routes related to Image management"},{"name":"Quantum Sync","description":"Routes related to quantum sync"},{"name":"Site","description":"Routes related to site objects"},{"name":"Utility","description":"Routes related to server metadata\/utility"},{"name":"Account","description":"Routes related to user account management."},{"name":"API Keys","description":"Manage API keys for the authenticated user"},{"name":"Organization","description":"Routes related to organization management"},{"name":"Delta Sync","description":"Field sync between a device and the cloud, across Quantum schema versions"},{"name":"Device Communications","description":"Routes related to PassiveLogic device communications. These are only accessible to official PassiveLogic devices, not users."}],"openapi":"3.0.1"}