From 5aa748cab5b115483d1a3369b5f3c6be8a2e244f Mon Sep 17 00:00:00 2001 From: cproudlock Date: Fri, 18 Sep 2026 20:18:06 -0400 Subject: [PATCH 1/6] Publish examples and query parameters, and stop the harvest leaking secrets The spec described 573 operations with a schema each and almost nothing a reader could copy: 12 operations carried an example, and no operation described a query parameter at all, because a Flask handler's query string is invisible to url_map. A route whose real interface is ?code= documented nothing. Both are now derived from evidence rather than written by hand. QUERY PARAMETERS come from each view's AST - request.args.get, the subscript form, getlist, and this repo's own helpers - and a read that does not resolve to a literal name is not emitted. 0 to 87 operations, 350 parameter objects. The enumerations some handlers validate (sort=assetnumber|hostname) are deliberately NOT published: deriving the accepted set reliably needs more than a literal read, and a wrong enum is exactly the invented contract this work avoids. EXAMPLES are harvested from the test suite, which already sends real bodies and asserts real responses, so they are transcripts rather than inventions. 12 to about 324. The harvest is re-runnable and optional; generation works without it. THE HARVEST PUBLISHED TWO REAL CREDENTIALS, and that is the important part of this commit. A bootstrap token and a kiosk setup code reached docs/openapi.json inside URL values - a file served at /api/docs on production and copied to a wiki. Neither scrubber layer could see them: the keys are 'scripturl' and 'url', which name no secret, and the opaque-value rule excludes ':' and '/' on purpose so that example URLs survive. Nothing was committed. Four changes came out of it: - a URL-credential rule that scrubs the parameter VALUE and keeps the URL, so ?t= is redacted and ?page=2&perpage=50 still reads as an example - redaction runs on the way OUT as well as on the way in. Harvested examples live in the inventory and a refresh preserves them, so a scrubber fixed after a harvest protects only future harvests while the stored copy keeps publishing. The published file can no longer carry a secret the current rules would catch, whatever the inventory holds - the leak gate now holds its own patterns instead of importing the generator's. Checked with the scrubber's own rules it could only ever assert that redact() ran, and it was green while both tokens sat in the file. It now knows the product's token prefixes, a URL-credential shape, and a crude entropy backstop. Replanting the exact credential that shipped fails it - a DELETE only declares a body when the HANDLER reads one, derived from its AST. A transcript proves the client sent something: the suite posts {} to routes that ignore it, and that published "this route reads a JSON body" on DELETE /api/endpoints/agentkey, whose handler never calls get_json. 249 operations still carry no example, because no test exercises them with a JSON body. The honest fix is a test, not a hand-written example, and the next harvest picks each one up for free. --- docs/api-inventory.json | 20267 +++++++++++++++++++++------ docs/openapi.json | 14255 ++++++++++++++++++- scripts/gen_openapi.py | 980 +- tests/test_api_inventory_parity.py | 60 + tests/test_openapi_examples.py | 637 + tests/test_openapi_spec.py | 55 + 6 files changed, 30953 insertions(+), 5301 deletions(-) create mode 100644 tests/test_openapi_examples.py diff --git a/docs/api-inventory.json b/docs/api-inventory.json index 53b0d85a..2484f037 100644 --- a/docs/api-inventory.json +++ b/docs/api-inventory.json @@ -1,4787 +1,15838 @@ [ - { - "surface": "core-apitokens", - "endpoints": [ + { + "surface": "core-apitokens", + "endpoints": [ + { + "method": "GET", + "path": "/api/apitokens", + "auth": "jwt", + "params": "not documented", + "purpose": "List the caller's own tokens. Admins may pass ?all=true for everyone's.", + "example": "curl http://localhost:5001/api/apitokens -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "all", + "type": "string", + "required": false, + "default": "false" + } + ], + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/apitokens", - "auth": "jwt", - "params": "not documented", - "purpose": "List the caller's own tokens. Admins may pass ?all=true for everyone's.", - "example": "curl http://localhost:5001/api/apitokens -H \"Authorization: Bearer $TOK\"" + "createddate": "2026-01-01T00:00:00.000000Z", + "displayprefix": "", + "expiresat": null, + "isactive": true, + "isexpired": false, + "lastusedat": null, + "name": "member token", + "resourcescopes": null, + "scopes": null, + "tokenid": 2, + "tokenprefix": "", + "userid": 2, + "username": "" }, { - "method": "POST", - "path": "/api/apitokens", - "auth": "permission:apitokens.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a token for the caller. Returns the full secret ONCE.", - "example": "curl -X POST http://localhost:5001/api/apitokens -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/apitokens/", - "auth": "permission:apitokens.create", - "params": "path: tokenid (int)", - "purpose": "Revoke (deactivate) a token. Own token, or any if admin.", - "example": "curl -X DELETE http://localhost:5001/api/apitokens/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/apitokens/", - "auth": "permission:apitokens.create", - "params": "path: tokenid (int); body: JSON (fields not yet documented)", - "purpose": "Rename, rescope, or deactivate a token. Own token, or any if admin.", - "example": "curl -X PUT http://localhost:5001/api/apitokens/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "createddate": "2026-01-01T00:00:00.000000Z", + "displayprefix": "", + "expiresat": null, + "isactive": true, + "isexpired": false, + "lastusedat": null, + "name": "admin token", + "resourcescopes": null, + "scopes": null, + "tokenid": 1, + "tokenprefix": "", + "userid": 1, + "username": "" } - ] - }, - { - "surface": "core-applications", - "endpoints": [ - { - "method": "GET", - "path": "/api/applications", - "auth": "permission:applications.view", - "params": "not documented", - "purpose": "List all applications.", - "example": "curl http://localhost:5001/api/applications -H \"Authorization: Bearer $TOK\"" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/apitokens", + "auth": "permission:apitokens.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a token for the caller. Returns the full secret ONCE.", + "example": "curl -X POST http://localhost:5001/api/apitokens -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "name": "display svc", + "resourcescopes": [ + "gea-shopfloor-display" + ], + "scopes": [ + "geenforce.fetch" + ] + }, + "responseexample": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "displayprefix": "", + "expiresat": null, + "isactive": true, + "isexpired": false, + "lastusedat": null, + "name": "display svc", + "resourcescopes": [ + "gea-shopfloor-display" + ], + "scopes": [ + "geenforce.fetch" + ], + "secret": "", + "tokenid": 1, + "tokenprefix": "", + "userid": 1, + "warning": "Save this token now. It will not be shown again. Store it somewhere safe." + }, + "message": "Token created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/apitokens/", + "auth": "permission:apitokens.create", + "params": "path: tokenid (int)", + "purpose": "Revoke (deactivate) a token. Own token, or any if admin.", + "example": "curl -X DELETE http://localhost:5001/api/apitokens/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "message": "Token revoked", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/apitokens/", + "auth": "permission:apitokens.create", + "params": "path: tokenid (int); body: JSON (fields not yet documented)", + "purpose": "Rename, rescope, or deactivate a token. Own token, or any if admin.", + "example": "curl -X PUT http://localhost:5001/api/apitokens/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "scopes": [ + "applications.edit" + ] + }, + "responseexample": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "displayprefix": "", + "expiresat": null, + "isactive": true, + "isexpired": false, + "lastusedat": null, + "name": "test token", + "resourcescopes": null, + "scopes": [ + "applications.edit" + ], + "tokenid": 1, + "tokenprefix": "", + "userid": 1 + }, + "message": "Token updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + } + ] + }, + { + "surface": "core-applications", + "endpoints": [ + { + "method": "GET", + "path": "/api/applications", + "auth": "permission:applications.view", + "params": "not documented", + "purpose": "List all applications.", + "example": "curl http://localhost:5001/api/applications -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "appname", + "type": "string", + "required": false + }, + { + "name": "installable", + "type": "string", + "required": false + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "showhidden", + "type": "string", + "required": false, + "default": "false" + } + ], + "responseexample": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/applications", + "auth": "permission:applications.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new application.", + "example": "curl -X POST http://localhost:5001/api/applications -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "appname": "Monitored App", + "ismonitorable": true, + "monitorlaunchpath": "C:\\Vendor\\app.exe", + "monitorprocessname": "app.exe" + }, + "responseexample": { + "data": { + "appdescription": null, + "appid": 1, + "applicationlink": null, + "applicationnotes": null, + "appname": "Fleet App", + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "documentationpath": null, + "image": null, + "installersha256": null, + "installpath": null, + "isactive": true, + "ishidden": false, + "isinstallable": false, + "islicenced": false, + "ismonitorable": false, + "isprinter": false, + "isrequired": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "monitorlaunchpath": null, + "monitorprocessname": null, + "policynote": "A configuration policy was created for this application, but it is not attached to any business policy and installs nothing until an admin adds it to one.", + "policyresult": "created", + "supportteamid": null, + "supportteamname": null, + "teamurl": null + }, + "message": "Application created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/applications/", + "auth": "permission:applications.delete", + "params": "path: app_id (int)", + "purpose": "Permanently delete an application, its versions, and its uploads.", + "example": "curl -X DELETE http://localhost:5001/api/applications/1 -H \"Authorization: Bearer $TOK\"", + "requestexample": { + "confirm": "OK" + }, + "responseexample": { + "data": { + "deactivatedpolicies": [ + "Required app: Doomed", + "Hand made push" + ], + "policyresult": "kept" + }, + "message": "Application deleted; deactivated 2 policy(ies) that referenced it: Required app: Doomed, Hand made push", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/applications/", + "auth": "permission:applications.view", + "params": "path: app_id (int)", + "purpose": "Get a single application with details.", + "example": "curl http://localhost:5001/api/applications/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "appdescription": null, + "appid": 1, + "applicationlink": null, + "applicationnotes": null, + "appname": "TeamApp", + "contacts": [ + { + "name": "Alice", + "sso": "a01" + } + ], + "createddate": "2026-01-01T00:00:00.000000Z", + "documentationpath": null, + "image": null, + "installedcount": 0, + "installersha256": null, + "installpath": null, + "isactive": true, + "ishidden": false, + "isinstallable": false, + "islicenced": false, + "ismonitorable": false, + "isprinter": false, + "isrequired": false, + "knowledgebase": [], + "modifieddate": "2026-01-01T00:00:00.000000Z", + "monitorlaunchpath": null, + "monitorprocessname": null, + "supportteamid": 1, + "supportteamname": "Controls", + "teamurl": "https://sn.example/controls", + "versions": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/applications/", + "auth": "permission:applications.edit", + "params": "path: app_id (int); body: JSON (fields not yet documented)", + "purpose": "Update an application.", + "example": "curl -X PUT http://localhost:5001/api/applications/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "ismonitorable": false, + "monitorlaunchpath": "", + "monitorprocessname": "" + }, + "responseexample": { + "data": { + "appdescription": null, + "appid": 1, + "applicationlink": null, + "applicationnotes": null, + "appname": "Test App", + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "documentationpath": null, + "image": null, + "installersha256": null, + "installpath": null, + "isactive": true, + "ishidden": false, + "isinstallable": false, + "islicenced": false, + "ismonitorable": false, + "isprinter": false, + "isrequired": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "monitorlaunchpath": null, + "monitorprocessname": null, + "policynote": "A configuration policy was created for this application, but it is not attached to any business policy and installs nothing until an admin adds it to one.", + "policyresult": "created", + "supportteamid": null, + "supportteamname": null, + "teamurl": null + }, + "message": "Application updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/applications//image", + "auth": "permission:applications.edit", + "params": "path: app_id (int)", + "purpose": "Remove an application's image and clear the field.", + "example": "curl -X DELETE http://localhost:5001/api/applications/1/image -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/applications//image", + "auth": "permission:applications.edit", + "params": "path: app_id (int); body: JSON (fields not yet documented)", + "purpose": "Upload (or replace) an application's image.", + "example": "curl -X POST http://localhost:5001/api/applications/1/image -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "appdescription": null, + "appid": 1, + "applicationlink": null, + "applicationnotes": null, + "appname": "Test App", + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "documentationpath": null, + "image": "/api/applications/image/application-1.png", + "installersha256": null, + "installpath": null, + "isactive": true, + "ishidden": false, + "isinstallable": false, + "islicenced": false, + "ismonitorable": false, + "isprinter": false, + "isrequired": false, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "monitorlaunchpath": null, + "monitorprocessname": null, + "supportteamid": null, + "supportteamname": null, + "teamurl": null + }, + "message": "Application image uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/applications//installed", + "auth": "permission:applications.view", + "params": "path: app_id (int)", + "purpose": "List all computers that have this application installed.", + "example": "curl http://localhost:5001/api/applications/1/installed -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "DELETE", + "path": "/api/applications//package", + "auth": "permission:applications.edit", + "params": "path: app_id (int)", + "purpose": "Remove an uploaded installer and clear the path it set.", + "example": "curl -X DELETE http://localhost:5001/api/applications/1/package -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "appdescription": null, + "appid": 1, + "applicationlink": null, + "applicationnotes": null, + "appname": "Test App", + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "documentationpath": null, + "image": null, + "installersha256": null, + "installpath": "", + "isactive": true, + "ishidden": false, + "isinstallable": false, + "islicenced": false, + "ismonitorable": false, + "isprinter": false, + "isrequired": false, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "monitorlaunchpath": null, + "monitorprocessname": null, + "supportteamid": null, + "supportteamname": null, + "teamurl": null + }, + "message": "Installer removed", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/applications//package", + "auth": "permission:applications.edit", + "params": "path: app_id (int); body: JSON (fields not yet documented)", + "purpose": "Upload (or replace) the installer for an application.", + "example": "curl -X POST http://localhost:5001/api/applications/1/package -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "appdescription": null, + "appid": 1, + "applicationlink": null, + "applicationnotes": null, + "appname": "Test App", + "appversionid": null, + "bytes": 130, + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "detectedversion": "", + "documentationpath": null, + "image": null, + "installersha256": "", + "installpath": "/api/applications/package/1/setup.exe", + "isactive": true, + "ishidden": false, + "isinstallable": false, + "islicenced": false, + "ismonitorable": false, + "isprinter": false, + "isrequired": false, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "monitorlaunchpath": null, + "monitorprocessname": null, + "sha256": "", + "supportteamid": null, + "supportteamname": null, + "teamurl": null, + "uploadedfilename": "setup.exe" + }, + "message": "Installer uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/applications//revert", + "auth": "permission:applications.edit", + "params": "path: app_id (int); body: JSON (fields not yet documented)", + "purpose": "Strip an application back to a catalogue record.", + "example": "curl -X POST http://localhost:5001/api/applications/1/revert -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "confirm": "OK" + }, + "responseexample": { + "data": { + "appdescription": null, + "appid": 1, + "applicationlink": null, + "applicationnotes": null, + "appname": "Test App", + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "documentationpath": null, + "image": "/api/applications/image/application-1.png", + "installersha256": null, + "installpath": null, + "isactive": true, + "ishidden": false, + "isinstallable": false, + "islicenced": false, + "ismonitorable": false, + "isprinter": false, + "isrequired": false, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "monitorlaunchpath": null, + "monitorprocessname": null, + "supportteamid": null, + "supportteamname": null, + "teamurl": null + }, + "message": "Application reverted to record", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/applications//versions", + "auth": "permission:applications.view", + "params": "path: app_id (int)", + "purpose": "List all versions for an application.", + "example": "curl http://localhost:5001/api/applications/1/versions -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/applications//versions", + "auth": "permission:applications.create", + "params": "path: app_id (int); body: JSON (fields not yet documented)", + "purpose": "Create a new version for an application.", + "example": "curl -X POST http://localhost:5001/api/applications/1/versions -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "sha256": "", + "silentargs": "/S /v/qn", + "version": "1.2.3" + }, + "responseexample": { + "data": { + "appid": 1, + "appversionid": 1, + "dateadded": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "notes": null, + "releasedate": null, + "sha256": "", + "silentargs": "/S /v/qn", + "version": "1.2.3" + }, + "message": "Version created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/applications/image/", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "path: filename (path)", + "purpose": "Serve an uploaded application image.", + "example": "curl http://localhost:5001/api/applications/image/example.png" + }, + { + "method": "GET", + "path": "/api/applications/machines/", + "auth": "permission:applications.view", + "params": "path: machine_id (int)", + "purpose": "List all applications installed on a computer.", + "example": "curl http://localhost:5001/api/applications/machines/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "POST", - "path": "/api/applications", - "auth": "permission:applications.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new application.", - "example": "curl -X POST http://localhost:5001/api/applications -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/applications/", - "auth": "permission:applications.delete", - "params": "path: app_id (int)", - "purpose": "Permanently delete an application, its versions, and its uploads.", - "example": "curl -X DELETE http://localhost:5001/api/applications/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/applications/", - "auth": "permission:applications.view", - "params": "path: app_id (int)", - "purpose": "Get a single application with details.", - "example": "curl http://localhost:5001/api/applications/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/applications/", - "auth": "permission:applications.edit", - "params": "path: app_id (int); body: JSON (fields not yet documented)", - "purpose": "Update an application.", - "example": "curl -X PUT http://localhost:5001/api/applications/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/applications//image", - "auth": "permission:applications.edit", - "params": "path: app_id (int)", - "purpose": "Remove an application's image and clear the field.", - "example": "curl -X DELETE http://localhost:5001/api/applications/1/image -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/applications//image", - "auth": "permission:applications.edit", - "params": "path: app_id (int); body: JSON (fields not yet documented)", - "purpose": "Upload (or replace) an application's image.", - "example": "curl -X POST http://localhost:5001/api/applications/1/image -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/applications//installed", - "auth": "permission:applications.view", - "params": "path: app_id (int)", - "purpose": "List all computers that have this application installed.", - "example": "curl http://localhost:5001/api/applications/1/installed -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "DELETE", - "path": "/api/applications//package", - "auth": "permission:applications.edit", - "params": "path: app_id (int)", - "purpose": "Remove an uploaded installer and clear the path it set.", - "example": "curl -X DELETE http://localhost:5001/api/applications/1/package -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/applications//package", - "auth": "permission:applications.edit", - "params": "path: app_id (int); body: JSON (fields not yet documented)", - "purpose": "Upload (or replace) the installer for an application.", - "example": "curl -X POST http://localhost:5001/api/applications/1/package -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/applications//revert", - "auth": "permission:applications.edit", - "params": "path: app_id (int); body: JSON (fields not yet documented)", - "purpose": "Strip an application back to a catalogue record.", - "example": "curl -X POST http://localhost:5001/api/applications/1/revert -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/applications//versions", - "auth": "permission:applications.view", - "params": "path: app_id (int)", - "purpose": "List all versions for an application.", - "example": "curl http://localhost:5001/api/applications/1/versions -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/applications//versions", - "auth": "permission:applications.create", - "params": "path: app_id (int); body: JSON (fields not yet documented)", - "purpose": "Create a new version for an application.", - "example": "curl -X POST http://localhost:5001/api/applications/1/versions -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/applications/image/", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "path: filename (path)", - "purpose": "Serve an uploaded application image.", - "example": "curl http://localhost:5001/api/applications/image/example.png" - }, - { - "method": "GET", - "path": "/api/applications/machines/", - "auth": "permission:applications.view", - "params": "path: machine_id (int)", - "purpose": "List all applications installed on a computer.", - "example": "curl http://localhost:5001/api/applications/machines/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/applications/machines/", - "auth": "permission:applications.create", - "params": "path: machine_id (int); body: JSON (fields not yet documented)", - "purpose": "Install an application on a computer.", - "example": "curl -X POST http://localhost:5001/api/applications/machines/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/applications/machines//", - "auth": "permission:applications.delete", - "params": "path: machine_id (int), app_id (int)", - "purpose": "Uninstall an application from a computer.", - "example": "curl -X DELETE http://localhost:5001/api/applications/machines/1/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/applications/machines//", - "auth": "permission:applications.edit", - "params": "path: machine_id (int), app_id (int); body: JSON (fields not yet documented)", - "purpose": "Update installed application (e.g., change version).", - "example": "curl -X PUT http://localhost:5001/api/applications/machines/1/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/applications/package/", - "auth": "permission:applications.download", - "params": "path: filename (path)", - "purpose": "Download an uploaded installer.", - "example": "curl http://localhost:5001/api/applications/package/example.png -H \"Authorization: Bearer $TOK\"" + "appdescription": null, + "appid": 1, + "appname": "Test App", + "appversionid": null, + "computerid": 1, + "id": 1, + "installeddate": "2026-01-01T00:00:00.000000Z", + "installedversion": "1.2.3", + "isactive": true } - ] - }, - { - "surface": "core-assets", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/applications/machines/", + "auth": "permission:applications.create", + "params": "path: machine_id (int); body: JSON (fields not yet documented)", + "purpose": "Install an application on a computer.", + "example": "curl -X POST http://localhost:5001/api/applications/machines/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/applications/machines//", + "auth": "permission:applications.delete", + "params": "path: machine_id (int), app_id (int)", + "purpose": "Uninstall an application from a computer.", + "example": "curl -X DELETE http://localhost:5001/api/applications/machines/1/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/applications/machines//", + "auth": "permission:applications.edit", + "params": "path: machine_id (int), app_id (int); body: JSON (fields not yet documented)", + "purpose": "Update installed application (e.g., change version).", + "example": "curl -X PUT http://localhost:5001/api/applications/machines/1/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/applications/package/", + "auth": "permission:applications.download", + "params": "path: filename (path)", + "purpose": "Download an uploaded installer.", + "example": "curl http://localhost:5001/api/applications/package/example.png -H \"Authorization: Bearer $TOK\"" + } + ] + }, + { + "surface": "core-assets", + "endpoints": [ + { + "method": "GET", + "path": "/api/assets", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "List all assets with filtering and pagination.", + "example": "curl http://localhost:5001/api/assets -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "businessunit_id", + "type": "integer", + "required": false + }, + { + "name": "businessunitid", + "type": "integer", + "required": false + }, + { + "name": "dir", + "type": "string", + "required": false, + "default": "asc" + }, + { + "name": "include_type_data", + "type": "string", + "required": false, + "default": "false" + }, + { + "name": "location_id", + "type": "integer", + "required": false + }, + { + "name": "locationid", + "type": "integer", + "required": false + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "sort", + "type": "string", + "required": false, + "default": "assetnumber" + }, + { + "name": "status_id", + "type": "integer", + "required": false + }, + { + "name": "statusid", + "type": "integer", + "required": false + }, + { + "name": "type", + "type": "string", + "required": false + }, + { + "name": "type_id", + "type": "integer", + "required": false + }, + { + "name": "typeid", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/assets", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "List all assets with filtering and pagination.", - "example": "curl http://localhost:5001/api/assets -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/assets", - "auth": "permission:assets.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new asset.", - "example": "curl -X POST http://localhost:5001/api/assets -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/assets/", - "auth": "permission:assets.delete", - "params": "path: asset_id (int)", - "purpose": "Delete (soft delete) an asset.", - "example": "curl -X DELETE http://localhost:5001/api/assets/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/assets/", - "auth": "permission:assets.view", - "params": "path: asset_id (int)", - "purpose": "Get a single asset with full details.", - "example": "curl http://localhost:5001/api/assets/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/assets/", - "auth": "permission:assets.edit", - "params": "path: asset_id (int); body: JSON (fields not yet documented)", - "purpose": "Update an asset.", - "example": "curl -X PUT http://localhost:5001/api/assets/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/assets//communications", - "auth": "permission:assets.view", - "params": "path: asset_id (int)", - "purpose": "Get all communications for an asset.", - "example": "curl http://localhost:5001/api/assets/1/communications -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/assets//relationships", - "auth": "permission:assets.view", - "params": "path: asset_id (int)", - "purpose": "Get all relationships for an asset.", - "example": "curl http://localhost:5001/api/assets/1/relationships -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/assets/lookup/", - "auth": "permission:assets.view", - "params": "path: assetnumber (string)", - "purpose": "Look up an asset by its asset number.", - "example": "curl http://localhost:5001/api/assets/lookup/x -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/assets/map", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "Get all assets with map positions for unified floor map display.", - "example": "curl http://localhost:5001/api/assets/map -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/assets/relationships", - "auth": "permission:assets.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a relationship between two assets.", - "example": "curl -X POST http://localhost:5001/api/assets/relationships -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/assets/relationships/", - "auth": "permission:assets.delete", - "params": "path: rel_id (int)", - "purpose": "Delete an asset relationship.", - "example": "curl -X DELETE http://localhost:5001/api/assets/relationships/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/assets/relationshiptypes", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "List all asset relationship types.", - "example": "curl http://localhost:5001/api/assets/relationshiptypes -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/assets/relationshiptypes", - "auth": "permission:assets.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new asset relationship type.", - "example": "curl -X POST http://localhost:5001/api/assets/relationshiptypes -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/assets/relationshiptypes/", - "auth": "permission:assets.delete", - "params": "path: type_id (int)", - "purpose": "Delete a relationship type. Refused if any relationship still uses it.", - "example": "curl -X DELETE http://localhost:5001/api/assets/relationshiptypes/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/assets/relationshiptypes/", - "auth": "permission:assets.edit", - "params": "path: type_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a relationship type.", - "example": "curl -X PUT http://localhost:5001/api/assets/relationshiptypes/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/assets/statuses", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "List all asset statuses.", - "example": "curl http://localhost:5001/api/assets/statuses -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/assets/statuses", - "auth": "permission:assets.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new asset status.", - "example": "curl -X POST http://localhost:5001/api/assets/statuses -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/assets/statuses/", - "auth": "permission:assets.delete", - "params": "path: status_id (int)", - "purpose": "Delete an asset status. Refused if any asset still uses it.", - "example": "curl -X DELETE http://localhost:5001/api/assets/statuses/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/assets/statuses/", - "auth": "permission:assets.view", - "params": "path: status_id (int)", - "purpose": "Get a single asset status.", - "example": "curl http://localhost:5001/api/assets/statuses/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/assets/statuses/", - "auth": "permission:assets.edit", - "params": "path: status_id (int); body: JSON (fields not yet documented)", - "purpose": "Update an asset status.", - "example": "curl -X PUT http://localhost:5001/api/assets/statuses/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/assets/types", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "List all asset types.", - "example": "curl http://localhost:5001/api/assets/types -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/assets/types", - "auth": "permission:assets.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new asset type.", - "example": "curl -X POST http://localhost:5001/api/assets/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/assets/types/", - "auth": "permission:assets.view", - "params": "path: type_id (int)", - "purpose": "Get a single asset type.", - "example": "curl http://localhost:5001/api/assets/types/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/assets/types/", - "auth": "permission:assets.edit", - "params": "path: type_id (int); body: JSON (fields not yet documented)", - "purpose": "Update an asset type's display fields (color/icon/description). The name, plugin, and table are structural and not editable here.", - "example": "curl -X PUT http://localhost:5001/api/assets/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "assetid": 1, + "assetnumber": "A-1", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "machine", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "mill", + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "serialnumber": "", + "statusid": 1 } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/assets", + "auth": "permission:assets.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new asset.", + "example": "curl -X POST http://localhost:5001/api/assets -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/assets/", + "auth": "permission:assets.delete", + "params": "path: asset_id (int)", + "purpose": "Delete (soft delete) an asset.", + "example": "curl -X DELETE http://localhost:5001/api/assets/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/assets/", + "auth": "permission:assets.view", + "params": "path: asset_id (int)", + "purpose": "Get a single asset with full details.", + "example": "curl http://localhost:5001/api/assets/1 -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "include_type_data", + "type": "string", + "required": false, + "default": "true" + } ] - }, - { - "surface": "core-auditlogs", - "endpoints": [ - { - "method": "GET", - "path": "/api/auditlogs", - "auth": "permission:audit.view", - "params": "not documented", - "purpose": "List audit logs with filtering and pagination.", - "example": "curl http://localhost:5001/api/auditlogs -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/assets/", + "auth": "permission:assets.edit", + "params": "path: asset_id (int); body: JSON (fields not yet documented)", + "purpose": "Update an asset.", + "example": "curl -X PUT http://localhost:5001/api/assets/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/assets//communications", + "auth": "permission:assets.view", + "params": "path: asset_id (int)", + "purpose": "Get all communications for an asset.", + "example": "curl http://localhost:5001/api/assets/1/communications -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/assets//relationships", + "auth": "permission:assets.view", + "params": "path: asset_id (int)", + "purpose": "Get all relationships for an asset.", + "example": "curl http://localhost:5001/api/assets/1/relationships -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "incoming": [ + { + "createddate": "2026-01-01T00:00:00.000000Z", + "inheritsposition": true, + "isactive": true, + "isdirectional": true, + "label": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "relationshipid": 1, + "relationshiptypeid": 1, + "relationshiptypename": "controls", + "sourceasset": { + "assetid": 1, + "assetnumber": "PC-1", + "assettypecolor": null, + "assettypeid": 2, + "assettypename": "computer", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "serialnumber": "", + "statusid": 1 + }, + "sourceassetid": 1, + "targetassetid": 2 + } + ], + "outgoing": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/assets/lookup/", + "auth": "permission:assets.view", + "params": "path: assetnumber (string)", + "purpose": "Look up an asset by its asset number.", + "example": "curl http://localhost:5001/api/assets/lookup/x -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/assets/map", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "Get all assets with map positions for unified floor map display.", + "example": "curl http://localhost:5001/api/assets/map -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "assettype", + "type": "string", + "required": false + }, + { + "name": "businessunitid", + "type": "integer", + "required": false + }, + { + "name": "locationid", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "statusid", + "type": "integer", + "required": false + }, + { + "name": "subtype", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": { + "assets": [ + { + "assetid": 1, + "assetnumber": "2007", + "assettype": "machine", + "assettypeid": 1, + "businessunit": null, + "businessunitid": null, + "displayname": "", + "levelid": null, + "location": null, + "locationid": null, + "mapx": 100, + "mapy": 100, + "name": null, + "primaryip": null, + "serialnumber": "", + "status": null, + "statuscolor": null, + "statusid": 1, + "typedata": { + "assetid": 1, + "controllermodelid": null, + "controllervendorid": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "islocationonly": false, + "lastmaintenancedate": null, + "machineid": 1, + "machinetypeid": null, + "maintenanceintervaldays": null, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextmaintenancedate": null, + "requiresmanualconfig": false, + "vendorid": null + } + }, + { + "assetid": 2, + "assetnumber": "2008", + "assettype": "machine", + "assettypeid": 1, + "businessunit": null, + "businessunitid": null, + "displayname": "", + "levelid": null, + "location": null, + "locationid": null, + "mapx": 110, + "mapy": 110, + "name": null, + "primaryip": null, + "serialnumber": "", + "status": null, + "statuscolor": null, + "statusid": 1, + "typedata": { + "assetid": 2, + "controllermodelid": null, + "controllervendorid": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "islocationonly": false, + "lastmaintenancedate": null, + "machineid": 2, + "machinetypeid": null, + "maintenanceintervaldays": null, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextmaintenancedate": null, + "requiresmanualconfig": false, + "vendorid": null + } + } + ], + "filters": { + "assettypes": [ + { + "assettype": "machine", + "assettypeid": 1, + "color": null, + "icon": null + } + ], + "businessunits": [], + "locations": [], + "statuses": [], + "subtypes": { + "Computer": [], + "Machine": [], + "Measuring Tool": [], + "Network Device": [], + "Printer": [] + } }, + "total": 2 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/assets/relationships", + "auth": "permission:assets.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a relationship between two assets.", + "example": "curl -X POST http://localhost:5001/api/assets/relationships -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "relationshiptypeid": 1, + "sourceassetid": 1, + "targetassetid": 2 + }, + "responseexample": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "inheritsposition": true, + "isactive": true, + "label": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "propagated": [ + { + "createddate": "2026-01-01T00:00:00.000000Z", + "inheritsposition": true, + "isactive": true, + "label": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "relationshipid": 3, + "relationshiptypeid": 1, + "sourceassetid": 1, + "targetassetid": 3 + } + ], + "propagatedcount": 1, + "relationshipid": 2, + "relationshiptypeid": 1, + "sourceassetid": 1, + "targetassetid": 2 + }, + "message": "Relationship created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/assets/relationships/", + "auth": "permission:assets.delete", + "params": "path: rel_id (int)", + "purpose": "Delete an asset relationship.", + "example": "curl -X DELETE http://localhost:5001/api/assets/relationships/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "message": "Relationship deleted", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/assets/relationshiptypes", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "List all asset relationship types.", + "example": "curl http://localhost:5001/api/assets/relationshiptypes -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/auditlogs/entity//", - "auth": "permission:audit.view", - "params": "path: entitytype (string), entityid (int)", - "purpose": "Get audit history for a specific entity.", - "example": "curl http://localhost:5001/api/auditlogs/entity/x/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/auditlogs/purge", - "auth": "permission:audit.purge", - "params": "body: JSON (fields not yet documented)", - "purpose": "Delete entries older than the retention window.", - "example": "curl -X POST http://localhost:5001/api/auditlogs/purge -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/auditlogs/retention", - "auth": "permission:audit.view", - "params": "not documented", - "purpose": "Size of the log and what the configured retention would remove.", - "example": "curl http://localhost:5001/api/auditlogs/retention -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/auditlogs/stats", - "auth": "permission:audit.view", - "params": "not documented", - "purpose": "Get audit log statistics.", - "example": "curl http://localhost:5001/api/auditlogs/stats -H \"Authorization: Bearer $TOK\"" + "color": null, + "description": null, + "isdirectional": false, + "propagatesthrough": [], + "relationshiptype": "WiFi", + "relationshiptypeid": 1 } - ] - }, - { - "surface": "core-auth", - "endpoints": [ - { - "method": "POST", - "path": "/api/auth/change-password", - "auth": "jwt", - "params": "body: JSON (fields not yet documented)", - "purpose": "Change the authenticated user's own password.", - "example": "curl -X POST http://localhost:5001/api/auth/change-password -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/assets/relationshiptypes", + "auth": "permission:assets.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new asset relationship type.", + "example": "curl -X POST http://localhost:5001/api/assets/relationshiptypes -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "isdirectional": false, + "relationshiptype": "Dualpath" + }, + "responseexample": { + "data": { + "color": null, + "description": null, + "isdirectional": true, + "propagatesthrough": [], + "relationshiptype": "Backup For", + "relationshiptypeid": 1 + }, + "message": "Relationship type created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/assets/relationshiptypes/", + "auth": "permission:assets.delete", + "params": "path: type_id (int)", + "purpose": "Delete a relationship type. Refused if any relationship still uses it.", + "example": "curl -X DELETE http://localhost:5001/api/assets/relationshiptypes/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/assets/relationshiptypes/", + "auth": "permission:assets.edit", + "params": "path: type_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a relationship type.", + "example": "curl -X PUT http://localhost:5001/api/assets/relationshiptypes/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "isdirectional": false + }, + "responseexample": { + "data": { + "color": null, + "description": null, + "isdirectional": false, + "propagatesthrough": [], + "relationshiptype": "USB", + "relationshiptypeid": 1 + }, + "message": "Relationship type updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/assets/statuses", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "List all asset statuses.", + "example": "curl http://localhost:5001/api/assets/statuses -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 }, - { - "method": "POST", - "path": "/api/auth/login", - "auth": "none (the login surface itself; rate limited, 5-strike lockout)", - "params": "body: JSON (fields not yet documented)", - "purpose": "Authenticate user and return JWT tokens.", - "example": "curl -X POST http://localhost:5001/api/auth/login -H 'Content-Type: application/json' -d '{}'" + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/assets/statuses", + "auth": "permission:assets.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new asset status.", + "example": "curl -X POST http://localhost:5001/api/assets/statuses -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/assets/statuses/", + "auth": "permission:assets.delete", + "params": "path: status_id (int)", + "purpose": "Delete an asset status. Refused if any asset still uses it.", + "example": "curl -X DELETE http://localhost:5001/api/assets/statuses/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/assets/statuses/", + "auth": "permission:assets.view", + "params": "path: status_id (int)", + "purpose": "Get a single asset status.", + "example": "curl http://localhost:5001/api/assets/statuses/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/assets/statuses/", + "auth": "permission:assets.edit", + "params": "path: status_id (int); body: JSON (fields not yet documented)", + "purpose": "Update an asset status.", + "example": "curl -X PUT http://localhost:5001/api/assets/statuses/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/assets/types", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "List all asset types.", + "example": "curl http://localhost:5001/api/assets/types -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 }, - { - "method": "POST", - "path": "/api/auth/logout", - "auth": "jwt", - "params": "body: JSON (fields not yet documented)", - "purpose": "Logout user (for frontend token cleanup).", - "example": "curl -X POST http://localhost:5001/api/auth/logout -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/assets/types", + "auth": "permission:assets.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new asset type.", + "example": "curl -X POST http://localhost:5001/api/assets/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/assets/types/", + "auth": "permission:assets.view", + "params": "path: type_id (int)", + "purpose": "Get a single asset type.", + "example": "curl http://localhost:5001/api/assets/types/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/assets/types/", + "auth": "permission:assets.edit", + "params": "path: type_id (int); body: JSON (fields not yet documented)", + "purpose": "Update an asset type's display fields (color/icon/description). The name, plugin, and table are structural and not editable here.", + "example": "curl -X PUT http://localhost:5001/api/assets/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + } + ] + }, + { + "surface": "core-auditlogs", + "endpoints": [ + { + "method": "GET", + "path": "/api/auditlogs", + "auth": "permission:audit.view", + "params": "not documented", + "purpose": "List audit logs with filtering and pagination.", + "example": "curl http://localhost:5001/api/auditlogs -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "action", + "type": "string", + "required": false + }, + { + "name": "entitytype", + "type": "string", + "required": false + }, + { + "name": "from_date", + "type": "string", + "required": false + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "perpage", + "type": "integer", + "required": false, + "default": 50 + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "to_date", + "type": "string", + "required": false + }, + { + "name": "userid", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 50, + "total": 0, + "totalpages": 0 }, - { - "method": "GET", - "path": "/api/auth/me", - "auth": "jwt", - "params": "not documented", - "purpose": "Get current authenticated user info.", - "example": "curl http://localhost:5001/api/auth/me -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/auth/refresh", - "auth": "jwt (refresh token)", - "params": "body: JSON (fields not yet documented)", - "purpose": "Refresh access token using refresh token.", - "example": "curl -X POST http://localhost:5001/api/auth/refresh -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/auditlogs/entity//", + "auth": "permission:audit.view", + "params": "path: entitytype (string), entityid (int)", + "purpose": "Get audit history for a specific entity.", + "example": "curl http://localhost:5001/api/auditlogs/entity/x/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/auditlogs/purge", + "auth": "permission:audit.purge", + "params": "body: JSON (fields not yet documented)", + "purpose": "Delete entries older than the retention window.", + "example": "curl -X POST http://localhost:5001/api/auditlogs/purge -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + }, + { + "method": "GET", + "path": "/api/auditlogs/retention", + "auth": "permission:audit.view", + "params": "not documented", + "purpose": "Size of the log and what the configured retention would remove.", + "example": "curl http://localhost:5001/api/auditlogs/retention -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "expired": 0, + "newest": "2026-01-01T00:00:00.000000", + "oldest": "2026-01-01T00:00:00.000000", + "retentiondays": 30, + "total": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/auditlogs/stats", + "auth": "permission:audit.view", + "params": "not documented", + "purpose": "Get audit log statistics.", + "example": "curl http://localhost:5001/api/auditlogs/stats -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "actions": {}, + "activeUsers": [], + "entities": {}, + "recentCount": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + } + ] + }, + { + "surface": "core-auth", + "endpoints": [ + { + "method": "POST", + "path": "/api/auth/change-password", + "auth": "jwt", + "params": "body: JSON (fields not yet documented)", + "purpose": "Change the authenticated user's own password.", + "example": "curl -X POST http://localhost:5001/api/auth/change-password -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "current_password": "", + "new_password": "" + }, + "responseexample": { + "data": { + "mustchangepassword": "" + }, + "message": "Password changed", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/auth/login", + "auth": "none (the login surface itself; rate limited, 5-strike lockout)", + "params": "body: JSON (fields not yet documented)", + "purpose": "Authenticate user and return JWT tokens.", + "example": "curl -X POST http://localhost:5001/api/auth/login -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "password": "", + "username": "" + }, + "responseexample": { + "data": { + "access_token": "", + "expires_in": 3600, + "refresh_token": "", + "token_type": "", + "user": { + "email": "", + "firstname": "", + "lastname": "", + "mustchangepassword": "", + "permissions": [ + "endpoints.runscript", + "endpoints.agentupdate", + "endpoints.view", + "" + ], + "roles": [ + "upgradeoperatorrole" + ], + "userid": 2, + "username": "" } - ] - }, - { - "surface": "core-businessunits", - "endpoints": [ + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/auth/logout", + "auth": "jwt", + "params": "body: JSON (fields not yet documented)", + "purpose": "Logout user (for frontend token cleanup).", + "example": "curl -X POST http://localhost:5001/api/auth/logout -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + }, + { + "method": "GET", + "path": "/api/auth/me", + "auth": "jwt", + "params": "not documented", + "purpose": "Get current authenticated user info.", + "example": "curl http://localhost:5001/api/auth/me -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "email": "", + "firstname": "", + "lastname": "", + "mustchangepassword": "", + "permissions": [ + "assets.view", + "assets.create", + "assets.edit", + "" + ], + "roles": [ + "admin" + ], + "userid": 1, + "username": "" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/auth/refresh", + "auth": "jwt (refresh token)", + "params": "body: JSON (fields not yet documented)", + "purpose": "Refresh access token using refresh token.", + "example": "curl -X POST http://localhost:5001/api/auth/refresh -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + } + ] + }, + { + "surface": "core-businessunits", + "endpoints": [ + { + "method": "GET", + "path": "/api/businessunits", + "auth": "jwt-optional (works signed out; the handler decides what shows)", + "params": "not documented", + "purpose": "List all business units.", + "example": "curl http://localhost:5001/api/businessunits -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "businessunit", + "type": "string", + "required": false + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/businessunits", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new business unit.", + "example": "curl -X POST http://localhost:5001/api/businessunits -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/businessunits/", + "auth": "jwt + role:admin", + "params": "path: bu_id (int)", + "purpose": "Delete (deactivate) a business unit.", + "example": "curl -X DELETE http://localhost:5001/api/businessunits/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/businessunits/", + "auth": "permission:assets.view", + "params": "path: bu_id (int)", + "purpose": "Get a single business unit.", + "example": "curl http://localhost:5001/api/businessunits/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/businessunits/", + "auth": "jwt + role:admin", + "params": "path: bu_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a business unit.", + "example": "curl -X PUT http://localhost:5001/api/businessunits/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + } + ] + }, + { + "surface": "core-collector", + "endpoints": [ + { + "method": "POST", + "path": "/api/collector/", + "auth": "api-key (per-plugin collector token)", + "params": "path: pluginname (string); body: JSON (fields not yet documented)", + "purpose": "Generic collector ingest for any plugin (ADR-006).", + "example": "curl -X POST http://localhost:5001/api/collector/x -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "hostname": "", + "queues": [ { - "method": "GET", - "path": "/api/businessunits", - "auth": "jwt-optional (works signed out; the handler decides what shows)", - "params": "not documented", - "purpose": "List all business units.", - "example": "curl http://localhost:5001/api/businessunits -H \"Authorization: Bearer $TOK\"" + "drivername": "HP Universal Printing PS", + "isdefault": true, + "portaddress": "", + "portname": "IP_10.20.0.11", + "queuename": "PRINTER-A" }, { - "method": "POST", - "path": "/api/businessunits", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new business unit.", - "example": "curl -X POST http://localhost:5001/api/businessunits -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "drivername": "HP Universal Printing PS", + "isdefault": false, + "portaddress": "", + "portname": "IP_10.20.0.12", + "queuename": "PRINTER-B" }, { - "method": "DELETE", - "path": "/api/businessunits/", - "auth": "jwt + role:admin", - "params": "path: bu_id (int)", - "purpose": "Delete (deactivate) a business unit.", - "example": "curl -X DELETE http://localhost:5001/api/businessunits/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/businessunits/", - "auth": "permission:assets.view", - "params": "path: bu_id (int)", - "purpose": "Get a single business unit.", - "example": "curl http://localhost:5001/api/businessunits/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/businessunits/", - "auth": "jwt + role:admin", - "params": "path: bu_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a business unit.", - "example": "curl -X PUT http://localhost:5001/api/businessunits/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "drivername": "HP Universal Printing PS", + "isdefault": false, + "portaddress": "", + "portname": "IP_10.20.0.99", + "queuename": "Reception Copier" } - ] - }, - { - "surface": "core-collector", - "endpoints": [ + ] + }, + "responseexample": { + "data": { + "accessprotocols": [], + "action": "updated", + "assetid": 2, + "identityvalue": "LEGACYPC", + "machinelinks": [], + "measuringtoollinkcount": 0, + "measuringtoollinks": [], + "partmarkers": [ + { + "assetid": 3, + "assetnumber": "LEGACYPC-PARTMARKER", + "operationassetid": null + } + ], + "printerlinkcount": 0, + "printerlinks": [], + "status": "ok", + "warnings": [ + "mapped ComputerType not found: Shopfloor PC", + "machine number '0615' is this PC's own asset number; marker not filed under an operation. Rename the PC asset to its hostname, or create the operation asset." + ] + }, + "message": "computers collector updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/collector/_schemas", + "auth": "jwt", + "params": "not documented", + "purpose": "List collector schemas for all enabled plugins (JWT-protected).", + "example": "curl http://localhost:5001/api/collector/_schemas -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/collector/apps", + "auth": "api-key (collector token, legacy shared key)", + "params": "body: JSON (fields not yet documented)", + "purpose": "Update installed applications for a PC (matched by hostname).", + "example": "curl -X POST http://localhost:5001/api/collector/apps -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "POST", + "path": "/api/collector/bulk", + "auth": "api-key (collector token, legacy shared key)", + "params": "body: JSON (fields not yet documented)", + "purpose": "Bulk update multiple PCs at once.", + "example": "curl -X POST http://localhost:5001/api/collector/bulk -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "pcs": [ { - "method": "POST", - "path": "/api/collector/", - "auth": "api-key (per-plugin collector token)", - "params": "path: pluginname (string); body: JSON (fields not yet documented)", - "purpose": "Generic collector ingest for any plugin (ADR-006).", - "example": "curl -X POST http://localhost:5001/api/collector/x -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/collector/_schemas", - "auth": "jwt", - "params": "not documented", - "purpose": "List collector schemas for all enabled plugins (JWT-protected).", - "example": "curl http://localhost:5001/api/collector/_schemas -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/collector/apps", - "auth": "api-key (collector token, legacy shared key)", - "params": "body: JSON (fields not yet documented)", - "purpose": "Update installed applications for a PC (matched by hostname).", - "example": "curl -X POST http://localhost:5001/api/collector/apps -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/collector/bulk", - "auth": "api-key (collector token, legacy shared key)", - "params": "body: JSON (fields not yet documented)", - "purpose": "Bulk update multiple PCs at once.", - "example": "curl -X POST http://localhost:5001/api/collector/bulk -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/collector/heartbeat", - "auth": "api-key (collector token, legacy shared key)", - "params": "body: JSON (fields not yet documented)", - "purpose": "Record PC online status / heartbeat (single or batch).", - "example": "curl -X POST http://localhost:5001/api/collector/heartbeat -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/collector/pc", - "auth": "api-key (collector token, legacy shared key)", - "params": "body: JSON (fields not yet documented)", - "purpose": "Update one PC from the collection script (matched by hostname).", - "example": "curl -X POST http://localhost:5001/api/collector/pc -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/collector/status", - "auth": "api-key (per-plugin collector token)", - "params": "not documented", - "purpose": "Check collector API status.", - "example": "curl http://localhost:5001/api/collector/status -H \"X-API-Key: $KEY\"" + "hostname": "" } - ] - }, - { - "surface": "core-customfields", - "endpoints": [ + ] + }, + "responseexample": { + "data": { + "errors": [], + "notfound": [ + "WJLEG01" + ], + "timestamp": "2026-01-01T00:00:00.000000", + "updated": 0 + }, + "message": "0 PC(s) updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/collector/heartbeat", + "auth": "api-key (collector token, legacy shared key)", + "params": "body: JSON (fields not yet documented)", + "purpose": "Record PC online status / heartbeat (single or batch).", + "example": "curl -X POST http://localhost:5001/api/collector/heartbeat -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "hostname": "" + }, + "responseexample": { + "data": { + "notfound": [ + "WJLEG01" + ], + "timestamp": "2026-01-01T00:00:00.000000", + "updated": 0 + }, + "message": "0 PC(s) heartbeat recorded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/collector/pc", + "auth": "api-key (collector token, legacy shared key)", + "params": "body: JSON (fields not yet documented)", + "purpose": "Update one PC from the collection script (matched by hostname).", + "example": "curl -X POST http://localhost:5001/api/collector/pc -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/collector/status", + "auth": "api-key (per-plugin collector token)", + "params": "not documented", + "purpose": "Check collector API status.", + "example": "curl http://localhost:5001/api/collector/status -H \"X-API-Key: $KEY\"", + "responseexample": { + "data": { + "endpoints": [ + "POST /api/collector/", + "GET /api/collector/_schemas", + "POST /api/collector/pc", + "" + ], + "status": "ok", + "timestamp": "2026-01-01T00:00:00.000000" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + } + ] + }, + { + "surface": "core-customfields", + "endpoints": [ + { + "method": "GET", + "path": "/api/customfields", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "List custom-field definitions. Filter by ?assettypeid=. ?active=false includes inactive ones.", + "example": "curl http://localhost:5001/api/customfields -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "assettypeid", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/customfields", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: customfields.create_field has no docstring.", + "example": "curl -X POST http://localhost:5001/api/customfields -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/customfields/", + "auth": "jwt + role:admin", + "params": "path: fieldid (int)", + "purpose": "UNDOCUMENTED: customfields.delete_field has no docstring.", + "example": "curl -X DELETE http://localhost:5001/api/customfields/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/customfields/", + "auth": "jwt + role:admin", + "params": "path: fieldid (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: customfields.update_field has no docstring.", + "example": "curl -X PUT http://localhost:5001/api/customfields/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/customfields/asset/", + "auth": "permission:assets.view", + "params": "path: assetid (int)", + "purpose": "Active custom fields for an asset's type, merged with its values.", + "example": "curl http://localhost:5001/api/customfields/asset/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/customfields/asset/", + "auth": "jwt + role:admin", + "params": "path: assetid (int); body: JSON (fields not yet documented)", + "purpose": "Upsert values for an asset. Body: {values: {fieldid: value, ...}}.", + "example": "curl -X PUT http://localhost:5001/api/customfields/asset/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + } + ] + }, + { + "surface": "core-dashboard", + "endpoints": [ + { + "method": "GET", + "path": "/api/dashboard", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "Get dashboard summary data (asset-based).", + "example": "curl http://localhost:5001/api/dashboard -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "activeassets": 14, + "bystatus": { + "In Use": 14 + }, + "counts": { + "machines": 0, + "measuringtools": 14, + "networkdevices": 0, + "pcs": 0, + "printers": 0, + "total": 14 + }, + "countsactiveonly": true, + "dualpathcollapsed": true, + "inactiveexcluded": 0, + "inrepair": 0, + "recent": [ + { + "assetid": 14, + "assetnumber": "MT-MAP-TD", + "assettype": "measuring_tool", + "createddate": "2026-01-01T00:00:00.000000Z" + }, + { + "assetid": 13, + "assetnumber": "MT-MAP-MIC", + "assettype": "measuring_tool", + "createddate": "2026-01-01T00:00:00.000000Z" + }, + { + "assetid": 12, + "assetnumber": "MT-MAP-CAL", + "assettype": "measuring_tool", + "createddate": "2026-01-01T00:00:00.000000Z" + }, + "" + ], + "statuses": { + "In Use": 14 + }, + "totalassets": 14, + "totalmachines": 0, + "totalmeasuringtool": 14, + "totalnetwork": 0, + "totalpc": 0, + "totalprinter": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/dashboard/capabilities", + "auth": "jwt-optional (works signed out; the handler decides what shows)", + "params": "not documented", + "purpose": "What THIS caller can reach, for chrome that would otherwise only 401.", + "example": "curl http://localhost:5001/api/dashboard/capabilities -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "cansearch": false + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/dashboard/health", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "not documented", + "purpose": "Health check endpoint (no auth required).", + "example": "curl http://localhost:5001/api/dashboard/health", + "responseexample": { + "data": { + "database": "healthy", + "status": "ok" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/dashboard/navigation", + "auth": "jwt-optional (works signed out; the handler decides what shows)", + "params": "not documented", + "purpose": "Get navigation items from all loaded plugins.", + "example": "curl http://localhost:5001/api/dashboard/navigation -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/customfields", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "List custom-field definitions. Filter by ?assettypeid=. ?active=false includes inactive ones.", - "example": "curl http://localhost:5001/api/customfields -H \"Authorization: Bearer $TOK\"" + "icon": "layout-dashboard", + "name": "Dashboard", + "permission": "assets.view", + "position": 0, + "route": "/" }, { - "method": "POST", - "path": "/api/customfields", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: customfields.create_field has no docstring.", - "example": "curl -X POST http://localhost:5001/api/customfields -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "icon": "map", + "name": "Map", + "permission": "assets.view", + "position": 4, + "route": "/map" }, { - "method": "DELETE", - "path": "/api/customfields/", - "auth": "jwt + role:admin", - "params": "path: fieldid (int)", - "purpose": "UNDOCUMENTED: customfields.delete_field has no docstring.", - "example": "curl -X DELETE http://localhost:5001/api/customfields/1 -H \"Authorization: Bearer $TOK\"" + "icon": "bell", + "name": "Notifications", + "permission": "notifications.view", + "plugin": "notifications", + "position": 5, + "route": "/notifications" + }, + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/dashboard/stats", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "Get detailed statistics by asset type.", + "example": "curl http://localhost:5001/api/dashboard/stats -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "bytype": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/dashboard/summary", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "Get dashboard summary data (asset-based).", + "example": "curl http://localhost:5001/api/dashboard/summary -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "activeassets": 0, + "bystatus": {}, + "counts": { + "machines": 0, + "measuringtools": 0, + "networkdevices": 0, + "pcs": 0, + "printers": 0, + "total": 0 + }, + "countsactiveonly": true, + "dualpathcollapsed": true, + "inactiveexcluded": 0, + "inrepair": 0, + "recent": [], + "statuses": {}, + "totalassets": 0, + "totalmachines": 0, + "totalmeasuringtool": 0, + "totalnetwork": 0, + "totalpc": 0, + "totalprinter": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/dashboard/widgets", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "Aggregate dashboard widget definitions from all enabled plugins.", + "example": "curl http://localhost:5001/api/dashboard/widgets -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ + { + "empty": "hide", + "endpoint": "/api/geenforce/dashboard/failures", + "id": "geenforce-failures", + "map": { + "detail": "entryname", + "link": "/pcs/{computerid}", + "meta": [ + { + "key": "message" + }, + { + "key": "exitcode", + "label": "exit" + } + ], + "timestamp": "receivedat", + "title": "hostname" + }, + "permission": "geenforce.manage", + "plugin": "geenforce", + "position": 10, + "render": "exceptions", + "severity": "critical", + "shortname": "Enforcement", + "title": "Enforcement failures", + "viewall": "/geenforce" }, { - "method": "PUT", - "path": "/api/customfields/", - "auth": "jwt + role:admin", - "params": "path: fieldid (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: customfields.update_field has no docstring.", - "example": "curl -X PUT http://localhost:5001/api/customfields/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "empty": "hide", + "endpoint": "/api/computers/dashboard/sharedmachines", + "id": "computers-sharedmachines", + "map": { + "detail": "pcs", + "link": "/machines/{assetid}", + "meta": [ + { + "key": "pccount", + "suffix": " PCs" + } + ], + "title": "machinenumber" + }, + "permission": "computers.view", + "plugin": "computers", + "position": 15, + "render": "exceptions", + "severity": "critical", + "shortname": "Duplicate machine no.", + "title": "Machine numbers on two PCs", + "viewall": "/machines" }, { - "method": "GET", - "path": "/api/customfields/asset/", - "auth": "permission:assets.view", - "params": "path: assetid (int)", - "purpose": "Active custom fields for an asset's type, merged with its values.", - "example": "curl http://localhost:5001/api/customfields/asset/1 -H \"Authorization: Bearer $TOK\"" + "empty": "hide", + "endpoint": "/api/endpoints/dashboard/stability", + "id": "endpoints-stability", + "map": { + "detail": "", + "link": "/pcs/{computerid}", + "meta": [ + { + "key": "eventcount", + "suffix": " events (48h)" + } + ], + "title": "hostname" + }, + "permission": "endpoints.fleet", + "plugin": "endpoints", + "position": 16, + "render": "exceptions", + "severity": "critical", + "shortname": "Crashes", + "title": "Endpoints with crashes or shutdowns", + "viewall": "/pcs" }, + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + } + ] + }, + { + "surface": "core-dashboarddefaults", + "endpoints": [ + { + "method": "GET", + "path": "/api/dashboarddefaults", + "auth": "permission:settings.view", + "params": "not documented", + "purpose": "List all visitor-IP -> business-unit mappings.", + "example": "curl http://localhost:5001/api/dashboarddefaults -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/dashboarddefaults", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a visitor-IP -> business-unit mapping.", + "example": "curl -X POST http://localhost:5001/api/dashboarddefaults -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "businessunitid": 1, + "description": "Materials lobby kiosk", + "ipaddress": "" + }, + "responseexample": { + "data": { + "businessunit": "Materials", + "businessunitid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "dashboarddefaultid": 1, + "description": "Materials lobby kiosk", + "displaypath": "/shopfloor", + "displayrole": "Dashboard", + "fqdn": "", + "ipaddress": "", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z" + }, + "message": "Mapping created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/dashboarddefaults/", + "auth": "jwt + role:admin", + "params": "path: default_id (int)", + "purpose": "Delete (deactivate) a mapping.", + "example": "curl -X DELETE http://localhost:5001/api/dashboarddefaults/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "message": "Mapping deleted", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/dashboarddefaults/", + "auth": "jwt + role:admin", + "params": "path: default_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a mapping.", + "example": "curl -X PUT http://localhost:5001/api/dashboarddefaults/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "description": "moved" + }, + "responseexample": { + "data": { + "businessunit": "Materials", + "businessunitid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "dashboarddefaultid": 1, + "description": "moved", + "displaypath": "/shopfloor", + "displayrole": "Dashboard", + "fqdn": "", + "ipaddress": "", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z" + }, + "message": "Mapping updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/dashboarddefaults/display-role", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "not documented", + "purpose": "Resolve what a single 'display' PC should show, from its own IP.", + "example": "curl http://localhost:5001/api/dashboarddefaults/display-role", + "queryparams": [ + { + "name": "fqdn", + "type": "string", + "required": false + }, + { + "name": "ipaddress", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": { + "businessunit": null, + "businessunitid": null, + "fqdn": "", + "ipaddress": "", + "path": "/parts-kiosk", + "role": "3DPrintRoom" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/dashboarddefaults/visitor-location", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "not documented", + "purpose": "Resolve the business unit for the calling display by its IP.", + "example": "curl http://localhost:5001/api/dashboarddefaults/visitor-location", + "queryparams": [ + { + "name": "fqdn", + "type": "string", + "required": false + }, + { + "name": "ipaddress", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": { + "businessunit": "Materials", + "businessunitid": 1, + "fqdn": "", + "ipaddress": "" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + } + ] + }, + { + "surface": "core-docs", + "endpoints": [ + { + "method": "GET", + "path": "/api/docs/", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "not documented", + "purpose": "Redoc page. url_for keeps the asset URLs correct under any mount.", + "example": "curl http://localhost:5001/api/docs/" + }, + { + "method": "GET", + "path": "/api/docs/llms.txt", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "not documented", + "purpose": "Concise LLM-oriented API guide.", + "example": "curl http://localhost:5001/api/docs/llms.txt" + }, + { + "method": "GET", + "path": "/api/docs/openapi.json", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "not documented", + "purpose": "The generated OpenAPI 3.1 spec (machine + LLM readable).", + "example": "curl http://localhost:5001/api/docs/openapi.json" + }, + { + "method": "GET", + "path": "/api/docs/redoc.standalone.js", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "not documented", + "purpose": "Vendored Redoc bundle (offline).", + "example": "curl http://localhost:5001/api/docs/redoc.standalone.js" + } + ] + }, + { + "surface": "core-locations", + "endpoints": [ + { + "method": "GET", + "path": "/api/locations", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "List all locations.", + "example": "curl http://localhost:5001/api/locations -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "locationname", + "type": "string", + "required": false + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/locations", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new location.", + "example": "curl -X POST http://localhost:5001/api/locations -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/locations/", + "auth": "jwt + role:admin", + "params": "path: location_id (int)", + "purpose": "Delete (deactivate) a location.", + "example": "curl -X DELETE http://localhost:5001/api/locations/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/locations/", + "auth": "permission:assets.view", + "params": "path: location_id (int)", + "purpose": "Get a single location.", + "example": "curl http://localhost:5001/api/locations/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/locations/", + "auth": "jwt + role:admin", + "params": "path: location_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a location.", + "example": "curl -X PUT http://localhost:5001/api/locations/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/locations/types", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "List location types. ?active=false includes inactive ones.", + "example": "curl http://localhost:5001/api/locations/types -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + } + ], + "responseexample": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/locations/types", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: locations.create_location_type has no docstring.", + "example": "curl -X POST http://localhost:5001/api/locations/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/locations/types/", + "auth": "jwt + role:admin", + "params": "path: type_id (int)", + "purpose": "UNDOCUMENTED: locations.delete_location_type has no docstring.", + "example": "curl -X DELETE http://localhost:5001/api/locations/types/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/locations/types/", + "auth": "jwt + role:admin", + "params": "path: type_id (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: locations.update_location_type has no docstring.", + "example": "curl -X PUT http://localhost:5001/api/locations/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + } + ] + }, + { + "surface": "core-maplevels", + "endpoints": [ + { + "method": "GET", + "path": "/api/maplevels", + "auth": "jwt-optional (works signed out; the handler decides what shows)", + "params": "not documented", + "purpose": "Every active level, grouped by building, in display order.", + "example": "curl http://localhost:5001/api/maplevels -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "buildings": [ + { + "buildingid": 1, + "buildingname": "Public Works", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "levels": [ + { + "assetcount": 0, + "blueprintdark": null, + "blueprintlight": "floorplan.svg", + "buildingid": 1, + "buildingname": "Public Works", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "isdefault": false, + "levelid": 1, + "levelname": "Ground", + "mapheight": 2550, + "mapwidth": 3300, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "sortorder": 1 + } + ], + "modifieddate": "2026-01-01T00:00:00.000000Z", + "sortorder": 1 + } + ], + "defaultlevelid": 1, + "totalplaced": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/maplevels", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: maplevels.create_level has no docstring.", + "example": "curl -X POST http://localhost:5001/api/maplevels -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/maplevels/", + "auth": "jwt + role:admin", + "params": "path: levelid (int)", + "purpose": "Deactivate a level, refusing while assets are still placed on it.", + "example": "curl -X DELETE http://localhost:5001/api/maplevels/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/maplevels/", + "auth": "jwt-optional (works signed out; the handler decides what shows)", + "params": "path: levelid (int)", + "purpose": "UNDOCUMENTED: maplevels.get_level has no docstring.", + "example": "curl http://localhost:5001/api/maplevels/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "blueprintdark": null, + "blueprintlight": "floorplan.svg", + "buildingid": 1, + "buildingname": "Public Works", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "isdefault": false, + "levelid": 1, + "levelname": "Ground", + "mapheight": 2550, + "mapwidth": 3300, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "sortorder": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PATCH|PUT", + "path": "/api/maplevels/", + "auth": "jwt + role:admin", + "params": "path: levelid (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: maplevels.update_level has no docstring.", + "example": "curl -X PATCH http://localhost:5001/api/maplevels/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "POST", + "path": "/api/maplevels//blueprint", + "auth": "jwt + role:admin", + "params": "path: levelid (int); body: JSON (fields not yet documented)", + "purpose": "Upload this level's blueprint for one theme.", + "example": "curl -X POST http://localhost:5001/api/maplevels/1/blueprint -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/maplevels//blueprint/", + "auth": "jwt-optional (works signed out; the handler decides what shows)", + "params": "path: levelid (int), filename (path)", + "purpose": "Serve a level's blueprint image. Public, for the same reason as above.", + "example": "curl http://localhost:5001/api/maplevels/1/blueprint/example.png -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/maplevels/buildings", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: maplevels.create_building has no docstring.", + "example": "curl -X POST http://localhost:5001/api/maplevels/buildings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "PATCH|PUT", + "path": "/api/maplevels/buildings/", + "auth": "jwt + role:admin", + "params": "path: buildingid (int); body: JSON (fields not yet documented)", + "purpose": "Rename or reorder a building. Levels move with it; nothing repositions.", + "example": "curl -X PATCH http://localhost:5001/api/maplevels/buildings/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + } + ] + }, + { + "surface": "core-mappositions", + "endpoints": [ + { + "method": "POST", + "path": "/api/mappositions/positions", + "auth": "permission:assets.edit", + "params": "body: JSON (fields not yet documented)", + "purpose": "Set positions for many assets at once.", + "example": "curl -X POST http://localhost:5001/api/mappositions/positions -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "positions": [ { - "method": "PUT", - "path": "/api/customfields/asset/", - "auth": "jwt + role:admin", - "params": "path: assetid (int); body: JSON (fields not yet documented)", - "purpose": "Upsert values for an asset. Body: {values: {fieldid: value, ...}}.", - "example": "curl -X PUT http://localhost:5001/api/customfields/asset/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "assetid": 1, + "levelid": 2, + "mapx": 42, + "mapy": 43 } - ] - }, - { - "surface": "core-dashboard", - "endpoints": [ + ] + }, + "responseexample": { + "data": { + "snapshotid": 1, + "updated": 1 + }, + "message": "1 position(s) saved", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/mappositions/snapshots", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "Snapshots, newest first. Metadata only - the positions are large.", + "example": "curl http://localhost:5001/api/mappositions/snapshots -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/mappositions/snapshots//restore", + "auth": "permission:assets.edit", + "params": "path: snapshotid (int); body: JSON (fields not yet documented)", + "purpose": "Put the positions in this snapshot back.", + "example": "curl -X POST http://localhost:5001/api/mappositions/snapshots/1/restore -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "restored": 3, + "skippedassetids": [] + }, + "message": "3 marker(s) restored", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/mappositions/transform", + "auth": "permission:assets.edit", + "params": "body: JSON (fields not yet documented)", + "purpose": "Move every placed marker on a level by a transform read off landmarks.", + "example": "curl -X POST http://localhost:5001/api/mappositions/transform -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "dryrun": false, + "landmarks": [ { - "method": "GET", - "path": "/api/dashboard", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "Get dashboard summary data (asset-based).", - "example": "curl http://localhost:5001/api/dashboard -H \"Authorization: Bearer $TOK\"" + "fromx": 0, + "fromy": 0, + "tox": 0, + "toy": 1450 }, { - "method": "GET", - "path": "/api/dashboard/capabilities", - "auth": "jwt-optional (works signed out; the handler decides what shows)", - "params": "not documented", - "purpose": "What THIS caller can reach, for chrome that would otherwise only 401.", - "example": "curl http://localhost:5001/api/dashboard/capabilities -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/dashboard/health", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "not documented", - "purpose": "Health check endpoint (no auth required).", - "example": "curl http://localhost:5001/api/dashboard/health" - }, - { - "method": "GET", - "path": "/api/dashboard/navigation", - "auth": "jwt-optional (works signed out; the handler decides what shows)", - "params": "not documented", - "purpose": "Get navigation items from all loaded plugins.", - "example": "curl http://localhost:5001/api/dashboard/navigation -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/dashboard/stats", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "Get detailed statistics by asset type.", - "example": "curl http://localhost:5001/api/dashboard/stats -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/dashboard/summary", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "Get dashboard summary data (asset-based).", - "example": "curl http://localhost:5001/api/dashboard/summary -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/dashboard/widgets", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "Aggregate dashboard widget definitions from all enabled plugins.", - "example": "curl http://localhost:5001/api/dashboard/widgets -H \"Authorization: Bearer $TOK\"" + "fromx": 1000, + "fromy": 1000, + "tox": 1000, + "toy": 2450 } - ] - }, - { - "surface": "core-dashboarddefaults", - "endpoints": [ - { - "method": "GET", - "path": "/api/dashboarddefaults", - "auth": "permission:settings.view", - "params": "not documented", - "purpose": "List all visitor-IP -> business-unit mappings.", - "example": "curl http://localhost:5001/api/dashboarddefaults -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/dashboarddefaults", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a visitor-IP -> business-unit mapping.", - "example": "curl -X POST http://localhost:5001/api/dashboarddefaults -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/dashboarddefaults/", - "auth": "jwt + role:admin", - "params": "path: default_id (int)", - "purpose": "Delete (deactivate) a mapping.", - "example": "curl -X DELETE http://localhost:5001/api/dashboarddefaults/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/dashboarddefaults/", - "auth": "jwt + role:admin", - "params": "path: default_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a mapping.", - "example": "curl -X PUT http://localhost:5001/api/dashboarddefaults/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/dashboarddefaults/display-role", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "not documented", - "purpose": "Resolve what a single 'display' PC should show, from its own IP.", - "example": "curl http://localhost:5001/api/dashboarddefaults/display-role" - }, - { - "method": "GET", - "path": "/api/dashboarddefaults/visitor-location", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "not documented", - "purpose": "Resolve the business unit for the calling display by its IP.", - "example": "curl http://localhost:5001/api/dashboarddefaults/visitor-location" + ], + "levelid": 1 + }, + "responseexample": { + "data": { + "assetcount": 3, + "dryrun": false, + "levelid": 1, + "moves": [ + { + "assetid": 1, + "assetnumber": "M000", + "fromx": 100, + "fromy": 100, + "name": null, + "outofbounds": false, + "tox": 100, + "toy": 1550 + }, + { + "assetid": 2, + "assetnumber": "M001", + "fromx": 1500, + "fromy": 1200, + "name": null, + "outofbounds": true, + "tox": 1500, + "toy": 2650 + }, + { + "assetid": 3, + "assetnumber": "M002", + "fromx": 3200, + "fromy": 2500, + "name": null, + "outofbounds": true, + "tox": 3200, + "toy": 3950 + } + ], + "outofboundscount": 2, + "snapshotid": 1, + "tolevelid": 1, + "transform": { + "offsetx": 0.0, + "offsety": 1450.0, + "scalex": 1.0, + "scaley": 1.0 } - ] - }, - { - "surface": "core-docs", - "endpoints": [ + }, + "message": "3 marker(s) moved; snapshot 1 can restore them", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/mappositions/verify", + "auth": "permission:assets.edit", + "params": "body: JSON (fields not yet documented)", + "purpose": "Mark markers as reviewed against the current drawing without moving them.", + "example": "curl -X POST http://localhost:5001/api/mappositions/verify -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "assetids": [ + 1 + ], + "unverify": true + }, + "responseexample": { + "data": { + "updated": 1 + }, + "message": "1 marker(s) unverified", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + } + ] + }, + { + "surface": "core-models", + "endpoints": [ + { + "method": "GET", + "path": "/api/models", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "List all vendor catalog models.", + "example": "curl http://localhost:5001/api/models -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "category", + "type": "string", + "required": false + }, + { + "name": "modelnumber", + "type": "string", + "required": false + }, + { + "name": "modeltype", + "type": "integer", + "required": false + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "vendor", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/models", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new model.", + "example": "curl -X POST http://localhost:5001/api/models -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/models/", + "auth": "jwt + role:admin", + "params": "path: model_id (int)", + "purpose": "Delete (deactivate) a model.", + "example": "curl -X DELETE http://localhost:5001/api/models/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/models/", + "auth": "permission:assets.view", + "params": "path: model_id (int)", + "purpose": "Get a single model.", + "example": "curl http://localhost:5001/api/models/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/models/", + "auth": "jwt + role:admin", + "params": "path: model_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a model.", + "example": "curl -X PUT http://localhost:5001/api/models/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/models//image", + "auth": "jwt + role:admin", + "params": "path: model_id (int)", + "purpose": "Clear a model image and delete the uploaded file if we own it.", + "example": "curl -X DELETE http://localhost:5001/api/models/1/image -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "documentationurl": null, + "imageurl": null, + "isactive": true, + "modelnumber": "TESTMODEL-1", + "modelnumberid": 1, + "modeltypeid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "vendorid": null + }, + "message": "Model image removed", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/models//image", + "auth": "jwt + role:admin", + "params": "path: model_id (int); body: JSON (fields not yet documented)", + "purpose": "Upload (or replace) the photo for a model.", + "example": "curl -X POST http://localhost:5001/api/models/1/image -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "documentationurl": null, + "imageurl": "/api/models/image/model-1.jpg", + "isactive": true, + "modelnumber": "TESTMODEL-1", + "modelnumberid": 1, + "modeltypeid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "vendorid": null + }, + "message": "Model image uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/models/image/", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "path: filename (path)", + "purpose": "Serve an uploaded model image (public - asset detail pages read it).", + "example": "curl http://localhost:5001/api/models/image/example.png" + } + ] + }, + { + "surface": "core-modeltypes", + "endpoints": [ + { + "method": "GET", + "path": "/api/modeltypes", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "List all model types with optional filtering.", + "example": "curl http://localhost:5001/api/modeltypes -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "category", + "type": "string", + "required": false + }, + { + "name": "modeltype", + "type": "string", + "required": false + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/modeltypes", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new model type.", + "example": "curl -X POST http://localhost:5001/api/modeltypes -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/modeltypes/", + "auth": "jwt + role:admin", + "params": "path: type_id (int)", + "purpose": "Delete (deactivate) a model type.", + "example": "curl -X DELETE http://localhost:5001/api/modeltypes/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/modeltypes/", + "auth": "permission:assets.view", + "params": "path: type_id (int)", + "purpose": "Get a single model type.", + "example": "curl http://localhost:5001/api/modeltypes/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/modeltypes/", + "auth": "jwt + role:admin", + "params": "path: type_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a model type.", + "example": "curl -X PUT http://localhost:5001/api/modeltypes/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + } + ] + }, + { + "surface": "core-operatingsystems", + "endpoints": [ + { + "method": "GET", + "path": "/api/operatingsystems", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "List all operating systems.", + "example": "curl http://localhost:5001/api/operatingsystems -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "osname", + "type": "string", + "required": false + }, + { + "name": "osversion", + "type": "string", + "required": false + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/operatingsystems", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new operating system.", + "example": "curl -X POST http://localhost:5001/api/operatingsystems -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/operatingsystems/", + "auth": "jwt + role:admin", + "params": "path: os_id (int)", + "purpose": "Delete (deactivate) an operating system.", + "example": "curl -X DELETE http://localhost:5001/api/operatingsystems/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/operatingsystems/", + "auth": "permission:assets.view", + "params": "path: os_id (int)", + "purpose": "Get a single operating system.", + "example": "curl http://localhost:5001/api/operatingsystems/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/operatingsystems/", + "auth": "jwt + role:admin", + "params": "path: os_id (int); body: JSON (fields not yet documented)", + "purpose": "Update an operating system.", + "example": "curl -X PUT http://localhost:5001/api/operatingsystems/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + } + ] + }, + { + "surface": "core-plugins", + "endpoints": [ + { + "method": "GET", + "path": "/api/plugins", + "auth": "permission:settings.view", + "params": "not documented", + "purpose": "List all discovered plugins (enabled or not) with their metadata and the framework contract version.", + "example": "curl http://localhost:5001/api/plugins -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/plugins/", + "auth": "jwt + role:admin", + "params": "path: name (string); body: JSON (fields not yet documented)", + "purpose": "Enable or disable a plugin. Takes effect on the next app restart for route/navigation changes.", + "example": "curl -X PUT http://localhost:5001/api/plugins/x -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/plugins/enabled", + "auth": "jwt-optional (works signed out; the handler decides what shows)", + "params": "not documented", + "purpose": "Return just the names of enabled plugins as a flat array.", + "example": "curl http://localhost:5001/api/plugins/enabled -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ + "backups", + "computers", + "employees", + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/plugins/settings-routes", + "auth": "permission:settings.view", + "params": "not documented", + "purpose": "Return settings routes contributed by all enabled plugins.", + "example": "curl http://localhost:5001/api/plugins/settings-routes -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "routes": [ + { + "component": "drawn.vue", + "hub": "model-catalog", + "name": "drawn-route", + "path": "drawn", + "tab": "Drawn" + }, + { + "component": ".plugins-staged/computers/views/OperatingSystemsList.vue", + "hub": "model-catalog", + "name": "model-catalog-os", + "path": "operatingsystems", + "tab": "Operating Systems" + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + } + ] + }, + { + "surface": "core-pluginui", + "endpoints": [ + { + "method": "GET", + "path": "/api/pluginui/asset-panels", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "Return the asset-detail panels matching one asset's type (get_asset_panels).", + "example": "curl http://localhost:5001/api/pluginui/asset-panels -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "assetid", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/docs/", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "not documented", - "purpose": "Redoc page. url_for keeps the asset URLs correct under any mount.", - "example": "curl http://localhost:5001/api/docs/" + "assettypes": [ + "computer" + ], + "empty": "No agent report yet.", + "endpoint": "/api/endpoints/asset/{assetid}/status", + "group": "details", + "id": "endpoints-status", + "plugin": "endpoints", + "position": 10, + "render": "keyvalue", + "title": "Endpoint status" }, { - "method": "GET", - "path": "/api/docs/llms.txt", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "not documented", - "purpose": "Concise LLM-oriented API guide.", - "example": "curl http://localhost:5001/api/docs/llms.txt" + "assettypes": [ + "computer" + ], + "collapsible": true, + "commandendpoint": "/api/endpoints/{assetid}/commands", + "empty": "No monitored services reported.", + "endpoint": "/api/endpoints/asset/{assetid}/services", + "facets": { + "key": "statelabel", + "order": [ + "Running", + "Stopped", + "Paused", + "" + ] + }, + "filter": { + "equals": "service", + "key": "kind" + }, + "group": "serviceprocess", + "id": "endpoints-tracked-services", + "map": { + "action": "action", + "badge": { + "color": "statecolor", + "label": "statelabel" + }, + "title": "name" + }, + "permission": "endpoints.command", + "plugin": "endpoints", + "position": 12, + "render": "list", + "status": { + "key": "statecolor", + "levels": { + "green": "ok", + "red": "critical", + "yellow": "warning" + } + }, + "title": "Tracked Services" }, { - "method": "GET", - "path": "/api/docs/openapi.json", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "not documented", - "purpose": "The generated OpenAPI 3.1 spec (machine + LLM readable).", - "example": "curl http://localhost:5001/api/docs/openapi.json" + "assettypes": [ + "computer" + ], + "collapsible": true, + "commandendpoint": "/api/endpoints/{assetid}/commands", + "empty": "No monitored processes reported.", + "endpoint": "/api/endpoints/asset/{assetid}/services", + "facets": { + "key": "statelabel", + "order": [ + "Running", + "Stopped", + "Paused", + "" + ] + }, + "filter": { + "equals": "process", + "key": "kind" + }, + "group": "serviceprocess", + "id": "endpoints-tracked-processes", + "map": { + "action": "action", + "badge": { + "color": "statecolor", + "label": "statelabel" + }, + "title": "name" + }, + "permission": "endpoints.command", + "plugin": "endpoints", + "position": 13, + "render": "list", + "status": { + "key": "statecolor", + "levels": { + "green": "ok", + "red": "critical", + "yellow": "warning" + } + }, + "title": "Tracked Processes" }, + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/pluginui/asset-presentation", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "Merge enabled plugins' asset-type presentation entries (hook).", + "example": "curl http://localhost:5001/api/pluginui/asset-presentation -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/docs/redoc.standalone.js", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "not documented", - "purpose": "Vendored Redoc bundle (offline).", - "example": "curl http://localhost:5001/api/docs/redoc.standalone.js" + "assettype": "measuring_tool", + "icon": "ruler", + "label": "Measuring Tool", + "plugin": "measuringtools", + "route": "/measuringtools/by-asset/{assetid}" } - ] - }, - { - "surface": "core-locations", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/pluginui/asset-type-descriptors", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "Merge enabled plugins' asset-type subtype descriptors.", + "example": "curl http://localhost:5001/api/pluginui/asset-type-descriptors -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/locations", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "List all locations.", - "example": "curl http://localhost:5001/api/locations -H \"Authorization: Bearer $TOK\"" + "assettype": "computer", + "edit_permission": "computers.manage", + "optional_tabs": [], + "plugin": "computers", + "subtype_color_field": "color", + "subtype_description_field": "description", + "subtype_endpoint": "/computers/types", + "subtype_id_field": "computertypeid", + "subtype_label_field": "computertype" }, { - "method": "POST", - "path": "/api/locations", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new location.", - "example": "curl -X POST http://localhost:5001/api/locations -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "assettype": "machine", + "edit_permission": "machines.manage", + "optional_tabs": [], + "plugin": "machines", + "subtype_color_field": "color", + "subtype_description_field": "description", + "subtype_endpoint": "/machines/types", + "subtype_id_field": "machinetypeid", + "subtype_label_field": "machinetype" }, { - "method": "DELETE", - "path": "/api/locations/", - "auth": "jwt + role:admin", - "params": "path: location_id (int)", - "purpose": "Delete (deactivate) a location.", - "example": "curl -X DELETE http://localhost:5001/api/locations/1 -H \"Authorization: Bearer $TOK\"" + "assettype": "measuring_tool", + "edit_permission": "measuringtools.manage", + "optional_tabs": [], + "plugin": "measuringtools", + "subtype_color_field": "color", + "subtype_description_field": "description", + "subtype_endpoint": "/measuringtools/types", + "subtype_id_field": "measuringtooltypeid", + "subtype_label_field": "name" + }, + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/pluginui/core-type-descriptors", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "Return type descriptors for core-owned non-asset types.", + "example": "curl http://localhost:5001/api/pluginui/core-type-descriptors -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ + { + "category": "location", + "edit_permission": "admin", + "group": "other", + "optional_tabs": [], + "subtype_color_field": "color", + "subtype_description_field": "description", + "subtype_endpoint": "/locations/types", + "subtype_id_field": "locationtypeid", + "subtype_label_field": "locationtype", + "type_name": "Location Type", + "type_name_plural": "Location Types" }, { - "method": "GET", - "path": "/api/locations/", - "auth": "permission:assets.view", - "params": "path: location_id (int)", - "purpose": "Get a single location.", - "example": "curl http://localhost:5001/api/locations/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/locations/", - "auth": "jwt + role:admin", - "params": "path: location_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a location.", - "example": "curl -X PUT http://localhost:5001/api/locations/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/locations/types", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "List location types. ?active=false includes inactive ones.", - "example": "curl http://localhost:5001/api/locations/types -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/locations/types", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: locations.create_location_type has no docstring.", - "example": "curl -X POST http://localhost:5001/api/locations/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/locations/types/", - "auth": "jwt + role:admin", - "params": "path: type_id (int)", - "purpose": "UNDOCUMENTED: locations.delete_location_type has no docstring.", - "example": "curl -X DELETE http://localhost:5001/api/locations/types/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/locations/types/", - "auth": "jwt + role:admin", - "params": "path: type_id (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: locations.update_location_type has no docstring.", - "example": "curl -X PUT http://localhost:5001/api/locations/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "category": "notification", + "custom_component": "NotificationTypesTab", + "edit_permission": "admin", + "group": "other", + "optional_tabs": [], + "subtype_color_field": "typecolor", + "subtype_description_field": "typedescription", + "subtype_endpoint": "/notifications/types", + "subtype_id_field": "notificationtypeid", + "subtype_label_field": "typename", + "type_name": "Notification Type", + "type_name_plural": "Notification Types" } - ] - }, - { - "surface": "core-maplevels", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/pluginui/map-overlays", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "Merge enabled plugins' map overlay declarations (get_map_overlays hook).", + "example": "curl http://localhost:5001/api/pluginui/map-overlays -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/maplevels", - "auth": "jwt-optional (works signed out; the handler decides what shows)", - "params": "not documented", - "purpose": "Every active level, grouped by building, in display order.", - "example": "curl http://localhost:5001/api/maplevels -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/maplevels", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: maplevels.create_level has no docstring.", - "example": "curl -X POST http://localhost:5001/api/maplevels -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/maplevels/", - "auth": "jwt + role:admin", - "params": "path: levelid (int)", - "purpose": "Deactivate a level, refusing while assets are still placed on it.", - "example": "curl -X DELETE http://localhost:5001/api/maplevels/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/maplevels/", - "auth": "jwt-optional (works signed out; the handler decides what shows)", - "params": "path: levelid (int)", - "purpose": "UNDOCUMENTED: maplevels.get_level has no docstring.", - "example": "curl http://localhost:5001/api/maplevels/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PATCH|PUT", - "path": "/api/maplevels/", - "auth": "jwt + role:admin", - "params": "path: levelid (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: maplevels.update_level has no docstring.", - "example": "curl -X PATCH http://localhost:5001/api/maplevels/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/maplevels//blueprint", - "auth": "jwt + role:admin", - "params": "path: levelid (int); body: JSON (fields not yet documented)", - "purpose": "Upload this level's blueprint for one theme.", - "example": "curl -X POST http://localhost:5001/api/maplevels/1/blueprint -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/maplevels//blueprint/", - "auth": "jwt-optional (works signed out; the handler decides what shows)", - "params": "path: levelid (int), filename (path)", - "purpose": "Serve a level's blueprint image. Public, for the same reason as above.", - "example": "curl http://localhost:5001/api/maplevels/1/blueprint/example.png -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/maplevels/buildings", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: maplevels.create_building has no docstring.", - "example": "curl -X POST http://localhost:5001/api/maplevels/buildings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "PATCH|PUT", - "path": "/api/maplevels/buildings/", - "auth": "jwt + role:admin", - "params": "path: buildingid (int); body: JSON (fields not yet documented)", - "purpose": "Rename or reorder a building. Levels move with it; nothing repositions.", - "example": "curl -X PATCH http://localhost:5001/api/maplevels/buildings/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "endpoint": "/api/measuringtools/map-overlay", + "id": "calibration-due", + "label": "Calibration due", + "legend": true, + "plugin": "measuringtools", + "style": "badge" } - ] - }, - { - "surface": "core-mappositions", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/pluginui/settings-cards", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "Merge enabled plugins' settings-catalog cards (get_settings_cards hook).", + "example": "curl http://localhost:5001/api/pluginui/settings-cards -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "POST", - "path": "/api/mappositions/positions", - "auth": "permission:assets.edit", - "params": "body: JSON (fields not yet documented)", - "purpose": "Set positions for many assets at once.", - "example": "curl -X POST http://localhost:5001/api/mappositions/positions -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "description": "Collection interval, share root for part-marker snapshots, staleness threshold and revision retention", + "group": "Machines", + "icon": "package", + "plugin": "backups", + "position": 21, + "title": "Backups", + "to": "/settings/backups" }, { - "method": "GET", - "path": "/api/mappositions/snapshots", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "Snapshots, newest first. Metadata only - the positions are large.", - "example": "curl http://localhost:5001/api/mappositions/snapshots -H \"Authorization: Bearer $TOK\"" + "description": "Download the collector script stamped with this site's URL and ranges", + "group": "Computers", + "icon": "download", + "plugin": "computers", + "position": 26, + "title": "Asset reporter", + "to": "/settings/collector" }, { - "method": "POST", - "path": "/api/mappositions/snapshots//restore", - "auth": "permission:assets.edit", - "params": "path: snapshotid (int); body: JSON (fields not yet documented)", - "purpose": "Put the positions in this snapshot back.", - "example": "curl -X POST http://localhost:5001/api/mappositions/snapshots/1/restore -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "description": "The shared agent key PCs use until they enroll, the site agent installer builder, and what the agent reports.", + "group": "Endpoints", + "icon": "key", + "plugin": "endpoints", + "position": 27, + "title": "Endpoints", + "to": "/settings/endpoints/agentkey" }, - { - "method": "POST", - "path": "/api/mappositions/transform", - "auth": "permission:assets.edit", - "params": "body: JSON (fields not yet documented)", - "purpose": "Move every placed marker on a level by a transform read off landmarks.", - "example": "curl -X POST http://localhost:5001/api/mappositions/transform -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + } + ] + }, + { + "surface": "core-reports", + "endpoints": [ + { + "method": "GET", + "path": "/api/reports", + "auth": "permission:reports.view", + "params": "not documented", + "purpose": "List all available reports.", + "example": "curl http://localhost:5001/api/reports -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "reports": [ + { + "category": "inventory", + "description": "Machine count grouped by machine type", + "endpoint": "/api/reports/machines-by-type", + "id": "machines-by-type", + "name": "Machines by Type" + }, + { + "category": "inventory", + "description": "Asset count grouped by status", + "endpoint": "/api/reports/assets-by-status", + "id": "assets-by-status", + "name": "Assets by Status" + }, + { + "category": "usage", + "description": "Most clicked knowledge base articles", + "endpoint": "/api/reports/kb-popularity", + "id": "kb-popularity", + "name": "KB Popularity" + }, + "" + ], + "total": 13 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/reports/asset-inventory", + "auth": "permission:reports.view", + "params": "not documented", + "purpose": "Report: Complete asset inventory summary.", + "example": "curl http://localhost:5001/api/reports/asset-inventory -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "businessunitid", + "type": "integer", + "required": false + }, + { + "name": "format", + "type": "string", + "required": false + }, + { + "name": "locationid", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": { + "data": { + "bylocation": [], + "bystatus": [], + "bytype": [] }, + "generated": "2026-01-01T00:00:00.000000", + "report": "asset_inventory", + "total": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/reports/assets-by-status", + "auth": "permission:reports.view", + "params": "not documented", + "purpose": "Report: Asset count grouped by status.", + "example": "curl http://localhost:5001/api/reports/assets-by-status -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "assettypeid", + "type": "integer", + "required": false + }, + { + "name": "businessunitid", + "type": "integer", + "required": false + }, + { + "name": "format", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": { + "data": [], + "generated": "2026-01-01T00:00:00.000000", + "report": "assets_by_status", + "total": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/reports/email", + "auth": "permission:reports.export", + "params": "body: JSON (fields not yet documented)", + "purpose": "Email a report's data as an HTML table (on-demand delivery).", + "example": "curl -X POST http://localhost:5001/api/reports/email -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "columns": [ { - "method": "POST", - "path": "/api/mappositions/verify", - "auth": "permission:assets.edit", - "params": "body: JSON (fields not yet documented)", - "purpose": "Mark markers as reviewed against the current drawing without moving them.", - "example": "curl -X POST http://localhost:5001/api/mappositions/verify -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "key": "vendor", + "label": "Vendor" } - ] - }, - { - "surface": "core-models", - "endpoints": [ + ], + "rows": [ { - "method": "GET", - "path": "/api/models", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "List all vendor catalog models.", - "example": "curl http://localhost:5001/api/models -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/models", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new model.", - "example": "curl -X POST http://localhost:5001/api/models -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/models/", - "auth": "jwt + role:admin", - "params": "path: model_id (int)", - "purpose": "Delete (deactivate) a model.", - "example": "curl -X DELETE http://localhost:5001/api/models/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/models/", - "auth": "permission:assets.view", - "params": "path: model_id (int)", - "purpose": "Get a single model.", - "example": "curl http://localhost:5001/api/models/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/models/", - "auth": "jwt + role:admin", - "params": "path: model_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a model.", - "example": "curl -X PUT http://localhost:5001/api/models/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/models//image", - "auth": "jwt + role:admin", - "params": "path: model_id (int)", - "purpose": "Clear a model image and delete the uploaded file if we own it.", - "example": "curl -X DELETE http://localhost:5001/api/models/1/image -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/models//image", - "auth": "jwt + role:admin", - "params": "path: model_id (int); body: JSON (fields not yet documented)", - "purpose": "Upload (or replace) the photo for a model.", - "example": "curl -X POST http://localhost:5001/api/models/1/image -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/models/image/", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "path: filename (path)", - "purpose": "Serve an uploaded model image (public - asset detail pages read it).", - "example": "curl http://localhost:5001/api/models/image/example.png" + "vendor": "Dell" } - ] - }, - { - "surface": "core-modeltypes", - "endpoints": [ + ], + "subject": "Warranty Report", + "to": "" + }, + "responseexample": { + "data": { + "reason": "notconfigured", + "sent": false + }, + "message": "Email is not configured (SMTP disabled or host unset).", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/reports/kb-popularity", + "auth": "permission:reports.view", + "params": "not documented", + "purpose": "Report: Most clicked knowledge base articles.", + "example": "curl http://localhost:5001/api/reports/kb-popularity -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "format", + "type": "string", + "required": false + }, + { + "name": "limit", + "type": "integer", + "required": false, + "default": 20 + } + ], + "responseexample": { + "data": { + "data": [], + "generated": "2026-01-01T00:00:00.000000", + "report": "kb_popularity", + "total": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/reports/machines-by-type", + "auth": "permission:reports.view", + "params": "not documented", + "purpose": "Report: Machine count grouped by machine type.", + "example": "curl http://localhost:5001/api/reports/machines-by-type -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "businessunitid", + "type": "integer", + "required": false + }, + { + "name": "format", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": { + "data": [], + "generated": "2026-01-01T00:00:00.000000", + "report": "machines_by_type", + "total": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/reports/pc-relationships", + "auth": "permission:reports.view", + "params": "not documented", + "purpose": "Report: PCs with relationships to shop floor machines.", + "example": "curl http://localhost:5001/api/reports/pc-relationships -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "format", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": { + "data": [ + { + "hostname": "", + "ip": "", + "machine_number": "4242", + "model": "", + "vendor": "" + } + ], + "generated": "2026-01-01T00:00:00.000000", + "report": "pc_relationships", + "total": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/reports/software-compliance", + "auth": "permission:reports.view", + "params": "not documented", + "purpose": "Report: Required applications vs installed (per PC).", + "example": "curl http://localhost:5001/api/reports/software-compliance -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "appid", + "type": "string", + "required": false + }, + { + "name": "format", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": { + "data": [], + "generated": "2026-01-01T00:00:00.000000", + "message": "No required applications defined", + "report": "software_compliance" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + } + ] + }, + { + "surface": "core-scripts", + "endpoints": [ + { + "method": "GET", + "path": "/api/scripts", + "auth": "permission:endpoints.runscript", + "params": "not documented", + "purpose": "List scripts (active by default).", + "example": "curl http://localhost:5001/api/scripts -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "search", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/scripts", + "auth": "permission:endpoints.runscript", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a script catalog entry (body uploaded separately).", + "example": "curl -X POST http://localhost:5001/api/scripts -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "scriptname": "autologon", + "workingdirectory": "C:\\Program Files\\Sysinternals" + }, + "responseexample": { + "data": { + "captureoutput": false, + "createddate": "2026-01-01T00:00:00.000000Z", + "errormode": "default", + "filename": null, + "hasbody": false, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "runcontext": "system", + "scriptdescription": null, + "scriptid": 1, + "scriptname": "autologon", + "sha256": null, + "workingdirectory": "C:\\Program Files\\Sysinternals" + }, + "message": "Script created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/scripts/", + "auth": "permission:endpoints.runscript", + "params": "path: script_id (int)", + "purpose": "UNDOCUMENTED: scripts.delete_script has no docstring.", + "example": "curl -X DELETE http://localhost:5001/api/scripts/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/scripts/", + "auth": "permission:endpoints.runscript", + "params": "path: script_id (int)", + "purpose": "UNDOCUMENTED: scripts.get_script has no docstring.", + "example": "curl http://localhost:5001/api/scripts/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/scripts/", + "auth": "permission:endpoints.runscript", + "params": "path: script_id (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: scripts.update_script has no docstring.", + "example": "curl -X PUT http://localhost:5001/api/scripts/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "scriptdescription": "now documented" + }, + "responseexample": { + "data": { + "captureoutput": true, + "createddate": "2026-01-01T00:00:00.000000Z", + "errormode": "default", + "filename": null, + "hasbody": false, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "runcontext": "system", + "scriptdescription": "now documented", + "scriptid": 1, + "scriptname": "keep-my-flag", + "sha256": null, + "workingdirectory": null + }, + "message": "Script updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/scripts//body", + "auth": "permission:endpoints.runscript", + "params": "path: script_id (int); body: JSON (fields not yet documented)", + "purpose": "Upload (or replace) the .ps1 body for a script.", + "example": "curl -X POST http://localhost:5001/api/scripts/1/body -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "bytes": 23, + "captureoutput": false, + "createddate": "2026-01-01T00:00:00.000000Z", + "errormode": "default", + "filename": "script-1.ps1", + "hasbody": true, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "runcontext": "system", + "scriptdescription": null, + "scriptid": 1, + "scriptname": "digest-check", + "sha256": "", + "uploadedfilename": "digest-check.ps1", + "workingdirectory": null + }, + "message": "Script body uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/scripts/body/", + "auth": "permission:endpoints.runscript", + "params": "path: filename (path)", + "purpose": "Operator-facing script body download (JWT/RBAC).", + "example": "curl http://localhost:5001/api/scripts/body/example.png -H \"Authorization: Bearer $TOK\"" + } + ] + }, + { + "surface": "core-search", + "endpoints": [ + { + "method": "GET", + "path": "/api/search", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "Global search across multiple entity types.", + "example": "curl http://localhost:5001/api/search -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "q", + "type": "string", + "required": false, + "default": "" + } + ], + "responseexample": { + "data": { + "counts": { + "knowledgebase": 2 + }, + "degraded": [ + "Employee" + ], + "query": "zzkeyword", + "results": [ + { + "id": 1, + "linkurl": "u", + "relevance": 25.0, + "subtitle": "Livesearchapp", + "title": "Zzrunbook for the live one", + "type": "knowledgebase", + "url": "/knowledgebase/1" + }, + { + "id": 3, + "linkurl": "u", + "relevance": 25.0, + "subtitle": null, + "title": "Zzrunbook with no topic", + "type": "knowledgebase", + "url": "/knowledgebase/3" + } + ], + "total": 2, + "total_all": 2 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/search/suggest", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "A better spelling for a query that found nothing, or null.", + "example": "curl http://localhost:5001/api/search/suggest -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "q", + "type": "string", + "required": false, + "default": "" + } + ], + "responseexample": { + "data": { + "query": "keyecne", + "suggestion": "keyence" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + } + ] + }, + { + "surface": "core-settings", + "endpoints": [ + { + "method": "GET", + "path": "/api/settings", + "auth": "jwt-optional (works signed out; the handler decides what shows)", + "params": "not documented", + "purpose": "List all settings, optionally filtered by category.", + "example": "curl http://localhost:5001/api/settings -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "category", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/modeltypes", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "List all model types with optional filtering.", - "example": "curl http://localhost:5001/api/modeltypes -H \"Authorization: Bearer $TOK\"" + "category": "auth", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "SAML Assertion Consumer Service URL", + "key": "saml_acs_url", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "settingid": 111, + "value": "", + "valuetype": "string" }, { - "method": "POST", - "path": "/api/modeltypes", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new model type.", - "example": "curl -X POST http://localhost:5001/api/modeltypes -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "category": "auth", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "SAML group name that grants admin role", + "key": "saml_admin_group", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "settingid": 114, + "value": "", + "valuetype": "string" }, { - "method": "DELETE", - "path": "/api/modeltypes/", - "auth": "jwt + role:admin", - "params": "path: type_id (int)", - "purpose": "Delete (deactivate) a model type.", - "example": "curl -X DELETE http://localhost:5001/api/modeltypes/1 -H \"Authorization: Bearer $TOK\"" + "category": "auth", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "Allow local username/password login when SAML is enabled", + "key": "saml_allow_local_login", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "settingid": 112, + "value": true, + "valuetype": "boolean" }, - { - "method": "GET", - "path": "/api/modeltypes/", - "auth": "permission:assets.view", - "params": "path: type_id (int)", - "purpose": "Get a single model type.", - "example": "curl http://localhost:5001/api/modeltypes/1 -H \"Authorization: Bearer $TOK\"" + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/settings", + "auth": "permission:settings.edit", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new setting (admin only).", + "example": "curl -X POST http://localhost:5001/api/settings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/settings/", + "auth": "jwt-optional (works signed out; the handler decides what shows)", + "params": "path: key (string)", + "purpose": "Get a single setting by key.", + "example": "curl http://localhost:5001/api/settings/x -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "category": "plugin.endpoints", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "Accept a one-line install bootstrap token in the ?t= query string as well as the X-Endpoint-Bootstrap-Token header. On (the default) keeps the pasted install commands and the Download .ps1 link workin...", + "key": "plugin.endpoints.bootstrapquerytoken", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "settingid": 1, + "value": false, + "valuetype": "boolean" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/settings/", + "auth": "permission:settings.edit", + "params": "path: key (string); body: JSON (fields not yet documented)", + "purpose": "Update a setting value.", + "example": "curl -X PUT http://localhost:5001/api/settings/x -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "value": "" + }, + "responseexample": { + "data": { + "category": "plugin.endpoints", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "Accept a one-line install bootstrap token in the ?t= query string as well as the X-Endpoint-Bootstrap-Token header. On (the default) keeps the pasted install commands and the Download .ps1 link workin...", + "key": "plugin.endpoints.bootstrapquerytoken", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "settingid": 1, + "value": false, + "valuetype": "boolean" + }, + "message": "Setting updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/settings/asset-import/import", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Validate uploaded CSVs and, when commit=true, load them.", + "example": "curl -X POST http://localhost:5001/api/settings/asset-import/import -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/settings/asset-import/script", + "auth": "jwt + role:admin", + "params": "not documented", + "purpose": "Download Collect-ShopDB.ps1 with this site's CSV path embedded.", + "example": "curl http://localhost:5001/api/settings/asset-import/script -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/settings/asset-import/templates", + "auth": "jwt + role:admin", + "params": "not documented", + "purpose": "List the importable CSV tables in the order they must be uploaded.", + "example": "curl http://localhost:5001/api/settings/asset-import/templates -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "tables": [ + { + "filename": "assetstatuses.csv", + "table": "assetstatuses" + }, + { + "filename": "assettypes.csv", + "table": "assettypes" + }, + { + "filename": "locationtypes.csv", + "table": "locationtypes" + }, + "" + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/settings/asset-import/templates/", + "auth": "jwt + role:admin", + "params": "path: tablename (string)", + "purpose": "Download a generated CSV template for a single importable table.", + "example": "curl http://localhost:5001/api/settings/asset-import/templates/x -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/settings/asset-import/winc/batches", + "auth": "jwt + role:admin", + "params": "not documented", + "purpose": "List applied/undone WINC import batches, newest first (for the undo UI).", + "example": "curl http://localhost:5001/api/settings/asset-import/winc/batches -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "batches": [ + { + "batchid": "", + "createdat": "2026-01-01 00:00:00.000000", + "createdby": "", + "kind": "winc-machine-adoption", + "rowcount": 1, + "status": "undone", + "undoneat": "2026-01-01 00:00:00.000000" + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/settings/asset-import/winc/commit", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Apply a WINC plan reversibly. Requires acknowledgment for needs-create.", + "example": "curl -X POST http://localhost:5001/api/settings/asset-import/winc/commit -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "batchid": "", + "blocking": false, + "committed": true, + "kind": "winc-machine-adoption", + "needscreate": { + "businessunit": { + "Large Parts": 1 + } }, - { - "method": "PUT", - "path": "/api/modeltypes/", - "auth": "jwt + role:admin", - "params": "path: type_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a model type.", - "example": "curl -X PUT http://localhost:5001/api/modeltypes/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "planhash": "", + "rowcount": 1, + "summary": { + "ambiguous_match": 0, + "businessunit_blank": 0, + "businessunit_needs-create": 1, + "businessunit_resolved": 0, + "businessunit_unresolved": 0, + "export_rows": 2, + "in_scope": 1, + "location_blank": 0, + "location_needs-create": 0, + "location_resolved": 1, + "location_unresolved": 0, + "non_workstation_rows": 1, + "skipped_no_existing_machine": 0 + }, + "uncreatable": {}, + "unresolved": {} + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/settings/asset-import/winc/preview", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Read-only WINC analysis: report what a commit would do. Writes nothing.", + "example": "curl -X POST http://localhost:5001/api/settings/asset-import/winc/preview -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "blocking": true, + "committed": false, + "kind": "winc-machine-adoption", + "needscreate": { + "businessunit": { + "Large Parts": 1 + }, + "location": { + "T6": 1 + } + }, + "planhash": "", + "rowcount": 1, + "summary": { + "ambiguous_match": 0, + "businessunit_blank": 0, + "businessunit_needs-create": 1, + "businessunit_resolved": 0, + "businessunit_unresolved": 0, + "export_rows": 1, + "in_scope": 1, + "location_blank": 0, + "location_needs-create": 1, + "location_resolved": 0, + "location_unresolved": 0, + "non_workstation_rows": 0, + "skipped_no_existing_machine": 0 + }, + "uncreatable": { + "location": { + "T6": 1 + } + }, + "unresolved": {} + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/settings/asset-import/winc/undo", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Reverse an applied WINC import batch by id. Conservative and reversible.", + "example": "curl -X POST http://localhost:5001/api/settings/asset-import/winc/undo -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "batchid": "" + }, + "responseexample": { + "data": { + "batchid": "", + "conflicts": [ + { + "column": null, + "operation": "insert", + "reason": "still referenced by dashboarddefaults.businessunitid", + "table": "businessunits", + "targetid": 1 + } + ], + "reverted": 3 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/settings/branding-logo", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Upload a branding logo image and point the matching setting at it.", + "example": "curl -X POST http://localhost:5001/api/settings/branding-logo -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "key": "site_favicon", + "value": "/api/settings/branding/logo-favicon.ico" + }, + "message": "Logo uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/settings/branding/", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "path: filename (path)", + "purpose": "Serve an uploaded branding logo (public - kiosks/print pages read it).", + "example": "curl http://localhost:5001/api/settings/branding/example.png" + }, + { + "method": "POST", + "path": "/api/settings/map-blueprint", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Upload a floor-map blueprint image and point the setting at it.", + "example": "curl -X POST http://localhost:5001/api/settings/map-blueprint -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/settings/map-blueprint/", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "path: filename (path)", + "purpose": "Serve an uploaded blueprint image (public - kiosks read it).", + "example": "curl http://localhost:5001/api/settings/map-blueprint/example.png" + }, + { + "method": "POST", + "path": "/api/settings/seed", + "auth": "permission:settings.edit", + "params": "body: JSON (fields not yet documented)", + "purpose": "Seed default settings if they don't exist.", + "example": "curl -X POST http://localhost:5001/api/settings/seed -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": {}, + "responseexample": { + "data": { + "created": 112 + }, + "message": "112 default settings created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/settings/test-email", + "auth": "permission:settings.edit", + "params": "body: JSON (fields not yet documented)", + "purpose": "Send a test email to verify SMTP configuration.", + "example": "curl -X POST http://localhost:5001/api/settings/test-email -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "to": "" + }, + "responseexample": { + "data": { + "error": "[Errno -2] Name or service not known", + "sent": false + }, + "message": "Test email failed: [Errno -2] Name or service not known", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + } + ] + }, + { + "surface": "core-setup", + "endpoints": [ + { + "method": "POST", + "path": "/api/setup/complete", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Flag the first-run setup as finished.", + "example": "curl -X POST http://localhost:5001/api/setup/complete -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + }, + { + "method": "POST", + "path": "/api/setup/create-admin", + "auth": "none (first run only: refused once an admin exists)", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create the first admin. Only works while there are zero users, setup has not been marked complete, and the caller proves operator control (setup token or a loopback request).", + "example": "curl -X POST http://localhost:5001/api/setup/create-admin -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/setup/needs-admin", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "not documented", + "purpose": "True when no users exist yet - the app's very first run. Public so the login screen can offer to create the first admin.", + "example": "curl http://localhost:5001/api/setup/needs-admin", + "responseexample": { + "data": { + "needsadmin": false + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/setup/seed-reference", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Seed core reference data + permissions + default settings. Idempotent - reuses the same routines as the `flask seed ...` CLI commands.", + "example": "curl -X POST http://localhost:5001/api/setup/seed-reference -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + }, + { + "method": "POST", + "path": "/api/setup/seed-starter", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Add common vendors that are not already present. Idempotent.", + "example": "curl -X POST http://localhost:5001/api/setup/seed-starter -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + } + ] + }, + { + "surface": "core-shopdbbackups", + "endpoints": [ + { + "method": "GET", + "path": "/api/shopdbbackups", + "auth": "jwt + role:admin", + "params": "not documented", + "purpose": "List every retained backup set, newest first, with a compatibility flag.", + "example": "curl http://localhost:5001/api/shopdbbackups -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [], + "meta": { + "count": 0, + "lastgoodat": null, + "requestid": "00000000", + "retention": { + "keepdaily": 5, + "keepmonthly": 6, + "keepweekly": 4 + }, + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/shopdbbackups", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Kick off an on-demand backup run.", + "example": "curl -X POST http://localhost:5001/api/shopdbbackups -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + }, + { + "method": "GET", + "path": "/api/shopdbbackups/", + "auth": "jwt + role:admin", + "params": "path: snapshotid (string)", + "purpose": "Return one set's full manifest plus a resolved compatibility verdict.", + "example": "curl http://localhost:5001/api/shopdbbackups/x -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/shopdbbackups//restore", + "auth": "jwt + role:admin", + "params": "path: snapshotid (string); body: JSON (fields not yet documented)", + "purpose": "Queue a restore of a set, gated on schema compatibility.", + "example": "curl -X POST http://localhost:5001/api/shopdbbackups/x/restore -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + }, + { + "method": "POST", + "path": "/api/shopdbbackups/finalize-restore", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Run the post-restore schema upgrade in the app context.", + "example": "curl -X POST http://localhost:5001/api/shopdbbackups/finalize-restore -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "queued": true, + "snapshotid": "wj-2026-09-06T120000Z" + }, + "message": "Schema upgrade queued.", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/shopdbbackups/history", + "auth": "jwt + role:admin", + "params": "not documented", + "purpose": "Return the recorded history of backup and restore runs, newest first.", + "example": "curl http://localhost:5001/api/shopdbbackups/history -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "backups": [], + "restores": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/shopdbbackups/settings", + "auth": "jwt + role:admin", + "params": "not documented", + "purpose": "Return destination + retention config for the settings pane.", + "example": "curl http://localhost:5001/api/shopdbbackups/settings -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "backupsroot": "/backups", + "destinationkind": "local", + "destinationkinds": [ + "local", + "smb", + "aws" + ], + "fields": {}, + "isarmed": true, + "retention": { + "keepdaily": 5, + "keepmonthly": 6, + "keepweekly": 4 } - ] - }, - { - "surface": "core-operatingsystems", - "endpoints": [ - { - "method": "GET", - "path": "/api/operatingsystems", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "List all operating systems.", - "example": "curl http://localhost:5001/api/operatingsystems -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/operatingsystems", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new operating system.", - "example": "curl -X POST http://localhost:5001/api/operatingsystems -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/operatingsystems/", - "auth": "jwt + role:admin", - "params": "path: os_id (int)", - "purpose": "Delete (deactivate) an operating system.", - "example": "curl -X DELETE http://localhost:5001/api/operatingsystems/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/operatingsystems/", - "auth": "permission:assets.view", - "params": "path: os_id (int)", - "purpose": "Get a single operating system.", - "example": "curl http://localhost:5001/api/operatingsystems/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/operatingsystems/", - "auth": "jwt + role:admin", - "params": "path: os_id (int); body: JSON (fields not yet documented)", - "purpose": "Update an operating system.", - "example": "curl -X PUT http://localhost:5001/api/operatingsystems/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/shopdbbackups/settings", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Persist the backup destination kind and its credential/config fields.", + "example": "curl -X PUT http://localhost:5001/api/shopdbbackups/settings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": {}, + "responseexample": { + "data": { + "backupsroot": "/backups", + "destinationkind": "local", + "destinationkinds": [ + "local", + "smb", + "aws" + ], + "fields": {}, + "isarmed": true, + "retention": { + "keepdaily": 5, + "keepmonthly": 6, + "keepweekly": 4 } - ] - }, - { - "surface": "core-plugins", - "endpoints": [ + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/shopdbbackups/status", + "auth": "jwt + role:admin", + "params": "not documented", + "purpose": "Return the live status of the most recent backup and restore runs.", + "example": "curl http://localhost:5001/api/shopdbbackups/status -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "backup": null, + "restore": null + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + } + ] + }, + { + "surface": "core-supportteams", + "endpoints": [ + { + "method": "GET", + "path": "/api/supportteams", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "List support teams (with contacts). ?active and ?teamname filters.", + "example": "curl http://localhost:5001/api/supportteams -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "teamname", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/plugins", - "auth": "permission:settings.view", - "params": "not documented", - "purpose": "List all discovered plugins (enabled or not) with their metadata and the framework contract version.", - "example": "curl http://localhost:5001/api/plugins -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/plugins/", - "auth": "jwt + role:admin", - "params": "path: name (string); body: JSON (fields not yet documented)", - "purpose": "Enable or disable a plugin. Takes effect on the next app restart for route/navigation changes.", - "example": "curl -X PUT http://localhost:5001/api/plugins/x -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/plugins/enabled", - "auth": "jwt-optional (works signed out; the handler decides what shows)", - "params": "not documented", - "purpose": "Return just the names of enabled plugins as a flat array.", - "example": "curl http://localhost:5001/api/plugins/enabled -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/plugins/settings-routes", - "auth": "permission:settings.view", - "params": "not documented", - "purpose": "Return settings routes contributed by all enabled plugins.", - "example": "curl http://localhost:5001/api/plugins/settings-routes -H \"Authorization: Bearer $TOK\"" + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": false, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "supportteamid": 1, + "teamname": "Retired", + "teamurl": null, + "webhookurl": null } - ] - }, - { - "surface": "core-pluginui", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/supportteams", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new support team.", + "example": "curl -X POST http://localhost:5001/api/supportteams -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "createddate": "2026-01-01 00:00:00", + "modifieddate": "2026-01-01T00:00:00", + "teamname": "Legacy" + }, + "responseexample": { + "data": { + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "supportteamid": 1, + "teamname": "Controls", + "teamurl": "https://servicenow.example/group/controls", + "webhookurl": null + }, + "message": "Support team created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/supportteams/", + "auth": "jwt + role:admin", + "params": "path: team_id (int)", + "purpose": "Delete a support team; 409 while any application references it.", + "example": "curl -X DELETE http://localhost:5001/api/supportteams/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "message": "Support team deleted", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/supportteams/", + "auth": "permission:assets.view", + "params": "path: team_id (int)", + "purpose": "Get a single support team with its contacts.", + "example": "curl http://localhost:5001/api/supportteams/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "contacts": [ + { + "contactid": 2, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "First", + "sortorder": 1, + "sso": "111", + "supportteamid": 1 + }, + { + "contactid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Second", + "sortorder": 2, + "sso": "222", + "supportteamid": 1 + } + ], + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "supportteamid": 1, + "teamname": "Controls", + "teamurl": null, + "webhookurl": null + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/supportteams/", + "auth": "jwt + role:admin", + "params": "path: team_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a support team.", + "example": "curl -X PUT http://localhost:5001/api/supportteams/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "teamname": "Controls Renamed", + "teamurl": "https://x.example" + }, + "responseexample": { + "data": { + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "supportteamid": 1, + "teamname": "Controls Renamed", + "teamurl": "https://x.example", + "webhookurl": null + }, + "message": "Support team updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/supportteams//contacts", + "auth": "jwt + role:admin", + "params": "path: team_id (int); body: JSON (fields not yet documented)", + "purpose": "Add a contact to a support team.", + "example": "curl -X POST http://localhost:5001/api/supportteams/1/contacts -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "createddate": "2026-01-01 00:00:00", + "modifieddate": "2026-01-01T00:00:00", + "name": "LegacyOwner" + }, + "responseexample": { + "data": { + "contactid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Second", + "sortorder": 2, + "sso": "222", + "supportteamid": 1 + }, + "message": "Contact created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/supportteams//contacts/", + "auth": "jwt + role:admin", + "params": "path: team_id (int), contact_id (int)", + "purpose": "Delete a support-team contact.", + "example": "curl -X DELETE http://localhost:5001/api/supportteams/1/contacts/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "message": "Contact deleted", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/supportteams//contacts/", + "auth": "jwt + role:admin", + "params": "path: team_id (int), contact_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a support-team contact.", + "example": "curl -X PUT http://localhost:5001/api/supportteams/1/contacts/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "name": "First Updated" + }, + "responseexample": { + "data": { + "contactid": 2, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "First Updated", + "sortorder": 1, + "sso": "111", + "supportteamid": 1 + }, + "message": "Contact updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + } + ] + }, + { + "surface": "core-users", + "endpoints": [ + { + "method": "GET", + "path": "/api/users", + "auth": "jwt + role:admin", + "params": "not documented", + "purpose": "List all users.", + "example": "curl http://localhost:5001/api/users -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/pluginui/asset-panels", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "Return the asset-detail panels matching one asset's type (get_asset_panels).", - "example": "curl http://localhost:5001/api/pluginui/asset-panels -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/pluginui/asset-presentation", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "Merge enabled plugins' asset-type presentation entries (hook).", - "example": "curl http://localhost:5001/api/pluginui/asset-presentation -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/pluginui/asset-type-descriptors", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "Merge enabled plugins' asset-type subtype descriptors.", - "example": "curl http://localhost:5001/api/pluginui/asset-type-descriptors -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/pluginui/core-type-descriptors", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "Return type descriptors for core-owned non-asset types.", - "example": "curl http://localhost:5001/api/pluginui/core-type-descriptors -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/pluginui/map-overlays", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "Merge enabled plugins' map overlay declarations (get_map_overlays hook).", - "example": "curl http://localhost:5001/api/pluginui/map-overlays -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/pluginui/settings-cards", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "Merge enabled plugins' settings-catalog cards (get_settings_cards hook).", - "example": "curl http://localhost:5001/api/pluginui/settings-cards -H \"Authorization: Bearer $TOK\"" + "createddate": "2026-01-01T00:00:00.000000Z", + "directpermissions": [], + "email": "", + "failedlogins": 0, + "firstname": "", + "isactive": true, + "islocked": false, + "lastlogindate": "2026-01-01T00:00:00Z", + "lastname": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "mustchangepassword": "", + "permissions": [ + "apitokens.create", + "applications.create", + "applications.delete", + "" + ], + "roles": [ + { + "color": null, + "roleid": 1, + "rolename": "admin" + } + ], + "userid": 1, + "username": "" } - ] - }, - { - "surface": "core-reports", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/users", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new user.", + "example": "curl -X POST http://localhost:5001/api/users -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "directpermissions": [ + "endpoints.runscript" + ], + "email": "", + "password": "", + "roles": [], + "sendwelcome": false, + "username": "" + }, + "responseexample": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "directpermissions": [ + "endpoints.restart" + ], + "email": "", + "failedlogins": 0, + "firstname": "", + "isactive": true, + "islocked": false, + "lastlogindate": null, + "lastname": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "mustchangepassword": "", + "permissions": [ + "applications.view", + "assets.view", + "backups.download", + "" + ], + "roles": [ + { + "color": "#0DCAF0", + "roleid": 3, + "rolename": "technician" + } + ], + "userid": 2, + "username": "", + "warning": "User created but the welcome email could not be sent." + }, + "message": "User created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/users/", + "auth": "jwt + role:admin", + "params": "path: userid (int)", + "purpose": "Delete a user.", + "example": "curl -X DELETE http://localhost:5001/api/users/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "message": "User deleted", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/users/", + "auth": "jwt", + "params": "path: userid (int)", + "purpose": "Get a single user.", + "example": "curl http://localhost:5001/api/users/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "directpermissions": [], + "email": "", + "failedlogins": 0, + "firstname": "", + "isactive": true, + "islocked": false, + "lastlogindate": "2026-01-01T00:00:00Z", + "lastname": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "mustchangepassword": "", + "permissions": [], + "roles": [], + "userid": 1, + "username": "" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/users/", + "auth": "jwt", + "params": "path: userid (int); body: JSON (fields not yet documented)", + "purpose": "Update a user.", + "example": "curl -X PUT http://localhost:5001/api/users/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "password": "" + }, + "responseexample": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "directpermissions": [], + "email": "", + "failedlogins": 0, + "firstname": "", + "isactive": true, + "islocked": false, + "lastlogindate": null, + "lastname": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "mustchangepassword": "", + "permissions": [], + "roles": [], + "userid": 2, + "username": "" + }, + "message": "User updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/users/permissions", + "auth": "jwt", + "params": "not documented", + "purpose": "List assignable permissions grouped by category.", + "example": "curl http://localhost:5001/api/users/permissions -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/users/roles", + "auth": "jwt", + "params": "not documented", + "purpose": "List all roles with their permissions.", + "example": "curl http://localhost:5001/api/users/roles -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/reports", - "auth": "permission:reports.view", - "params": "not documented", - "purpose": "List all available reports.", - "example": "curl http://localhost:5001/api/reports -H \"Authorization: Bearer $TOK\"" + "color": null, + "description": "Administrator", + "grantable": null, + "isadmin": true, + "isguest": false, + "permissions": [], + "roleid": 1, + "rolename": "admin", + "usercount": 1 }, { - "method": "GET", - "path": "/api/reports/asset-inventory", - "auth": "permission:reports.view", - "params": "not documented", - "purpose": "Report: Complete asset inventory summary.", - "example": "curl http://localhost:5001/api/reports/asset-inventory -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/reports/assets-by-status", - "auth": "permission:reports.view", - "params": "not documented", - "purpose": "Report: Asset count grouped by status.", - "example": "curl http://localhost:5001/api/reports/assets-by-status -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/reports/email", - "auth": "permission:reports.export", - "params": "body: JSON (fields not yet documented)", - "purpose": "Email a report's data as an HTML table (on-demand delivery).", - "example": "curl -X POST http://localhost:5001/api/reports/email -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/reports/kb-popularity", - "auth": "permission:reports.view", - "params": "not documented", - "purpose": "Report: Most clicked knowledge base articles.", - "example": "curl http://localhost:5001/api/reports/kb-popularity -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/reports/machines-by-type", - "auth": "permission:reports.view", - "params": "not documented", - "purpose": "Report: Machine count grouped by machine type.", - "example": "curl http://localhost:5001/api/reports/machines-by-type -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/reports/pc-relationships", - "auth": "permission:reports.view", - "params": "not documented", - "purpose": "Report: PCs with relationships to shop floor machines.", - "example": "curl http://localhost:5001/api/reports/pc-relationships -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/reports/software-compliance", - "auth": "permission:reports.view", - "params": "not documented", - "purpose": "Report: Required applications vs installed (per PC).", - "example": "curl http://localhost:5001/api/reports/software-compliance -H \"Authorization: Bearer $TOK\"" + "color": "#6C757D", + "description": "Anyone who is not signed in. Holds nothing by default; grant it a permission to open that up to people with no account.", + "grantable": [ + "applications.view", + "assets.view", + "computers.view", + "" + ], + "isadmin": false, + "isguest": true, + "permissions": [], + "roleid": 2, + "rolename": "guest", + "usercount": 0 } - ] - }, - { - "surface": "core-scripts", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/users/roles", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new role.", + "example": "curl -X POST http://localhost:5001/api/users/roles -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "color": "#ff8800", + "description": "colorful", + "permissions": [], + "rolename": "painters" + }, + "responseexample": { + "data": { + "color": "#ff8800", + "description": "colorful", + "permissions": [], + "roleid": 2, + "rolename": "painters" + }, + "message": "Role created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/users/roles/", + "auth": "jwt + role:admin", + "params": "path: roleid (int)", + "purpose": "Delete a role.", + "example": "curl -X DELETE http://localhost:5001/api/users/roles/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/users/roles/", + "auth": "jwt + role:admin", + "params": "path: roleid (int); body: JSON (fields not yet documented)", + "purpose": "Update a role.", + "example": "curl -X PUT http://localhost:5001/api/users/roles/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "permissions": [ + "reports.view" + ] + }, + "responseexample": { + "data": { + "color": "#6C757D", + "description": "Anyone who is not signed in. Holds nothing by default; grant it a permission to open that up to people with no account.", + "permissions": [ + "reports.view" + ], + "roleid": 2, + "rolename": "guest" + }, + "message": "Role updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + } + ] + }, + { + "surface": "core-vendors", + "endpoints": [ + { + "method": "GET", + "path": "/api/vendors", + "auth": "permission:assets.view", + "params": "not documented", + "purpose": "List all vendors.", + "example": "curl http://localhost:5001/api/vendors -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "vendor", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/scripts", - "auth": "permission:endpoints.runscript", - "params": "not documented", - "purpose": "List scripts (active by default).", - "example": "curl http://localhost:5001/api/scripts -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/scripts", - "auth": "permission:endpoints.runscript", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a script catalog entry (body uploaded separately).", - "example": "curl -X POST http://localhost:5001/api/scripts -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/scripts/", - "auth": "permission:endpoints.runscript", - "params": "path: script_id (int)", - "purpose": "UNDOCUMENTED: scripts.delete_script has no docstring.", - "example": "curl -X DELETE http://localhost:5001/api/scripts/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/scripts/", - "auth": "permission:endpoints.runscript", - "params": "path: script_id (int)", - "purpose": "UNDOCUMENTED: scripts.get_script has no docstring.", - "example": "curl http://localhost:5001/api/scripts/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/scripts/", - "auth": "permission:endpoints.runscript", - "params": "path: script_id (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: scripts.update_script has no docstring.", - "example": "curl -X PUT http://localhost:5001/api/scripts/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/scripts//body", - "auth": "permission:endpoints.runscript", - "params": "path: script_id (int); body: JSON (fields not yet documented)", - "purpose": "Upload (or replace) the .ps1 body for a script.", - "example": "curl -X POST http://localhost:5001/api/scripts/1/body -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/scripts/body/", - "auth": "permission:endpoints.runscript", - "params": "path: filename (path)", - "purpose": "Operator-facing script body download (JWT/RBAC).", - "example": "curl http://localhost:5001/api/scripts/body/example.png -H \"Authorization: Bearer $TOK\"" + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "supportemail": "", + "supportphone": "", + "vendor": "Acme", + "vendorid": 1, + "website": null } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/vendors", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new vendor.", + "example": "curl -X POST http://localhost:5001/api/vendors -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "createddate": "2026-01-01 00:00:00", + "modifieddate": "2026-01-01T00:00:00", + "vendor": "LegacyVendor" + }, + "responseexample": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "supportemail": "", + "supportphone": "", + "vendor": "Unscoped Vendor", + "vendorid": 1, + "website": null + }, + "message": "Vendor created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/vendors/", + "auth": "jwt + role:admin", + "params": "path: vendor_id (int)", + "purpose": "Delete (deactivate) a vendor.", + "example": "curl -X DELETE http://localhost:5001/api/vendors/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/vendors/", + "auth": "permission:assets.view", + "params": "path: vendor_id (int)", + "purpose": "Get a single vendor.", + "example": "curl http://localhost:5001/api/vendors/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/vendors/", + "auth": "jwt + role:admin", + "params": "path: vendor_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a vendor.", + "example": "curl -X PUT http://localhost:5001/api/vendors/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + } + ] + }, + { + "surface": "plugin-backups", + "endpoints": [ + { + "method": "GET", + "path": "/api/backups/asset/", + "auth": "permission:backups.view", + "params": "path: assetid (int)", + "purpose": "Revision history for one asset, newest first.", + "example": "curl http://localhost:5001/api/backups/asset/1 -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "kind", + "type": "string", + "required": false + } ] - }, - { - "surface": "core-search", - "endpoints": [ + }, + { + "method": "GET", + "path": "/api/backups/asset//info", + "auth": "permission:backups.view", + "params": "path: assetid (int)", + "purpose": "A kind's 'at a glance' card, built from its LATEST revision.", + "example": "curl http://localhost:5001/api/backups/asset/1/info -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "kind", + "type": "string", + "required": false + } + ] + }, + { + "method": "GET", + "path": "/api/backups/dashboard/stale", + "auth": "permission:backups.view", + "params": "not documented", + "purpose": "Chains whose backup has stopped running.", + "example": "curl http://localhost:5001/api/backups/dashboard/stale -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/backups/kinds", + "auth": "permission:backups.view", + "params": "not documented", + "purpose": "Registered backup kinds, for UI that needs to enumerate them.", + "example": "curl http://localhost:5001/api/backups/kinds -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/search", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "Global search across multiple entity types.", - "example": "curl http://localhost:5001/api/search -H \"Authorization: Bearer $TOK\"" + "assettypes": [ + "machine" + ], + "displayname": "", + "formats": [ + { + "ext": ".reg", + "hint": "Restore with the Load... button in the NTLARS settings dialog.", + "id": "ntlars", + "label": "NTLARS Load... (.reg)", + "mimetype": "application/octet-stream" + }, + { + "ext": ".reg", + "hint": "Use when importing outside NTLARS on a 64-bit machine.", + "id": "wow6432node", + "label": "Direct reg import (.reg, WOW6432Node)", + "mimetype": "application/octet-stream" + } + ], + "key": "ntlars", + "storagebackend": "shopdb" }, { - "method": "GET", - "path": "/api/search/suggest", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "A better spelling for a query that found nothing, or null.", - "example": "curl http://localhost:5001/api/search/suggest -H \"Authorization: Bearer $TOK\"" + "assettypes": [ + "machine" + ], + "displayname": "", + "formats": [], + "key": "partmarker", + "storagebackend": "share" } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/backups/revisions/", + "auth": "permission:backups.view", + "params": "path: backuprevisionid (int)", + "purpose": "One revision, including its parsed payload where there is one.", + "example": "curl http://localhost:5001/api/backups/revisions/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/backups/revisions//diff", + "auth": "permission:backups.view", + "params": "path: backuprevisionid (int)", + "purpose": "Diff two revisions of the same asset+kind.", + "example": "curl http://localhost:5001/api/backups/revisions/1/diff -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "against", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": { + "againstid": 1, + "backuprevisionid": 2, + "changecount": 1, + "changes": [ + { + "after": "OKUMA", + "aftertype": "REG_SZ", + "before": "", + "beforetype": "REG_SZ", + "change": "changed", + "keypath": "General", + "valuename": "Cnc" + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/backups/revisions//download", + "auth": "permission:backups.download", + "params": "path: backuprevisionid (int)", + "purpose": "Render a revision back to its native file format.", + "example": "curl http://localhost:5001/api/backups/revisions/1/download -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "format", + "type": "string", + "required": false + } ] - }, - { - "surface": "core-settings", - "endpoints": [ + } + ] + }, + { + "surface": "plugin-computers", + "endpoints": [ + { + "method": "GET", + "path": "/api/computers", + "auth": "permission:computers.view", + "params": "not documented", + "purpose": "List all computers with filtering and pagination.", + "example": "curl http://localhost:5001/api/computers -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "assetnumber", + "type": "string", + "required": false + }, + { + "name": "businessunit_id", + "type": "integer", + "required": false + }, + { + "name": "businessunitid", + "type": "integer", + "required": false + }, + { + "name": "dir", + "type": "string", + "required": false, + "default": "asc" + }, + { + "name": "location_id", + "type": "integer", + "required": false + }, + { + "name": "locationid", + "type": "integer", + "required": false + }, + { + "name": "os_id", + "type": "integer", + "required": false + }, + { + "name": "osid", + "type": "integer", + "required": false + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "shopfloor", + "type": "string", + "required": false + }, + { + "name": "sort", + "type": "string", + "required": false, + "default": "hostname" + }, + { + "name": "type_id", + "type": "integer", + "required": false + }, + { + "name": "typeid", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/settings", - "auth": "jwt-optional (works signed out; the handler decides what shows)", - "params": "not documented", - "purpose": "List all settings, optionally filtered by category.", - "example": "curl http://localhost:5001/api/settings -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/settings", - "auth": "permission:settings.edit", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new setting (admin only).", - "example": "curl -X POST http://localhost:5001/api/settings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/settings/", - "auth": "jwt-optional (works signed out; the handler decides what shows)", - "params": "path: key (string)", - "purpose": "Get a single setting by key.", - "example": "curl http://localhost:5001/api/settings/x -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/settings/", - "auth": "permission:settings.edit", - "params": "path: key (string); body: JSON (fields not yet documented)", - "purpose": "Update a setting value.", - "example": "curl -X PUT http://localhost:5001/api/settings/x -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/settings/asset-import/import", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Validate uploaded CSVs and, when commit=true, load them.", - "example": "curl -X POST http://localhost:5001/api/settings/asset-import/import -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/settings/asset-import/script", - "auth": "jwt + role:admin", - "params": "not documented", - "purpose": "Download Collect-ShopDB.ps1 with this site's CSV path embedded.", - "example": "curl http://localhost:5001/api/settings/asset-import/script -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/settings/asset-import/templates", - "auth": "jwt + role:admin", - "params": "not documented", - "purpose": "List the importable CSV tables in the order they must be uploaded.", - "example": "curl http://localhost:5001/api/settings/asset-import/templates -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/settings/asset-import/templates/", - "auth": "jwt + role:admin", - "params": "path: tablename (string)", - "purpose": "Download a generated CSV template for a single importable table.", - "example": "curl http://localhost:5001/api/settings/asset-import/templates/x -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/settings/asset-import/winc/batches", - "auth": "jwt + role:admin", - "params": "not documented", - "purpose": "List applied/undone WINC import batches, newest first (for the undo UI).", - "example": "curl http://localhost:5001/api/settings/asset-import/winc/batches -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/settings/asset-import/winc/commit", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Apply a WINC plan reversibly. Requires acknowledgment for needs-create.", - "example": "curl -X POST http://localhost:5001/api/settings/asset-import/winc/commit -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/settings/asset-import/winc/preview", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Read-only WINC analysis: report what a commit would do. Writes nothing.", - "example": "curl -X POST http://localhost:5001/api/settings/asset-import/winc/preview -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/settings/asset-import/winc/undo", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Reverse an applied WINC import batch by id. Conservative and reversible.", - "example": "curl -X POST http://localhost:5001/api/settings/asset-import/winc/undo -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/settings/branding-logo", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Upload a branding logo image and point the matching setting at it.", - "example": "curl -X POST http://localhost:5001/api/settings/branding-logo -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/settings/branding/", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "path: filename (path)", - "purpose": "Serve an uploaded branding logo (public - kiosks/print pages read it).", - "example": "curl http://localhost:5001/api/settings/branding/example.png" - }, - { - "method": "POST", - "path": "/api/settings/map-blueprint", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Upload a floor-map blueprint image and point the setting at it.", - "example": "curl -X POST http://localhost:5001/api/settings/map-blueprint -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/settings/map-blueprint/", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "path: filename (path)", - "purpose": "Serve an uploaded blueprint image (public - kiosks read it).", - "example": "curl http://localhost:5001/api/settings/map-blueprint/example.png" - }, - { - "method": "POST", - "path": "/api/settings/seed", - "auth": "permission:settings.edit", - "params": "body: JSON (fields not yet documented)", - "purpose": "Seed default settings if they don't exist.", - "example": "curl -X POST http://localhost:5001/api/settings/seed -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/settings/test-email", - "auth": "permission:settings.edit", - "params": "body: JSON (fields not yet documented)", - "purpose": "Send a test email to verify SMTP configuration.", - "example": "curl -X POST http://localhost:5001/api/settings/test-email -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "accessmethods": [], + "assetid": 1, + "assetnumber": "PC-1", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "computer", + "businessunitid": null, + "computer": { + "accessprotocolnames": [], + "assetid": 1, + "azuredevicecategory": null, + "azuredeviceid": null, + "azureobjectid": null, + "computerid": 1, + "computertypeid": 1, + "computertypename": "Standard", + "createddate": "2026-01-01T00:00:00.000000Z", + "hostname": "", + "isactive": true, + "lastboottime": null, + "lastreporteddate": null, + "loggedinuser": null, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "osid": null, + "vendorid": null + }, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 } - ] - }, - { - "surface": "core-setup", - "endpoints": [ + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/computers", + "auth": "permission:computers.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create new computer (creates both Asset and Computer records).", + "example": "curl -X POST http://localhost:5001/api/computers -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "assetnumber": "PC-IMPORT-1", + "createddate": "2026-01-01 00:00:00", + "hostname": "", + "modifieddate": "2026-01-01T00:00:00" + }, + "responseexample": { + "data": { + "accessmethods": [], + "assetid": 1, + "assetnumber": "AUDIT-PC-1", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "computer", + "businessunitid": null, + "computer": { + "accessprotocolnames": [], + "assetid": 1, + "azuredevicecategory": null, + "azuredeviceid": null, + "azureobjectid": null, + "computerid": 1, + "computertypeid": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "hostname": "", + "isactive": true, + "lastboottime": null, + "lastreporteddate": null, + "loggedinuser": null, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "osid": null, + "vendorid": null + }, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statuscolor": null, + "statusid": 1, + "statusname": "In Use" + }, + "message": "Computer created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/computers/", + "auth": "permission:computers.delete", + "params": "path: computer_id (int)", + "purpose": "Delete (soft delete) computer.", + "example": "curl -X DELETE http://localhost:5001/api/computers/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/computers/", + "auth": "permission:computers.view", + "params": "path: computer_id (int)", + "purpose": "Get a single computer with full details.", + "example": "curl http://localhost:5001/api/computers/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "accessmethods": [], + "assetid": 1, + "assetnumber": "PC-2001", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "computer", + "businessunitid": null, + "communications": [], + "computer": { + "accessprotocolnames": [], + "assetid": 1, + "azuredevicecategory": null, + "azuredeviceid": null, + "azureobjectid": null, + "computerid": 1, + "computertypeid": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "hostname": "", + "imageurl": "/api/models/images/model-1.png", + "isactive": true, + "lastboottime": null, + "lastreporteddate": null, + "loggedinuser": null, + "modelname": "IMG-MODEL-1", + "modelnumberid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "osid": null, + "vendorid": null + }, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/computers/", + "auth": "permission:computers.edit", + "params": "path: computer_id (int); body: JSON (fields not yet documented)", + "purpose": "Update computer (both Asset and Computer records).", + "example": "curl -X PUT http://localhost:5001/api/computers/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "gaugelabreference": "GL-2", + "maintenancereference": "MNT-2" + }, + "responseexample": { + "data": { + "accessmethods": [ + { + "id": 1, + "link": "rdp://auditpc1", + "name": "RDP", + "port": null, + "portoverride": null, + "protocolid": 1, + "scheme": "rdp" + } + ], + "assetid": 1, + "assetnumber": "AUDIT-PC-1", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "computer", + "businessunitid": null, + "computer": { + "accessprotocolnames": [ + "RDP" + ], + "assetid": 1, + "azuredevicecategory": null, + "azuredeviceid": null, + "azureobjectid": null, + "computerid": 1, + "computertypeid": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "hostname": "", + "isactive": true, + "lastboottime": null, + "lastreporteddate": null, + "loggedinuser": null, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "osid": null, + "vendorid": null + }, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statuscolor": null, + "statusid": 1, + "statusname": "In Use" + }, + "message": "Computer updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/computers//apps", + "auth": "permission:computers.view", + "params": "path: computer_id (int)", + "purpose": "Get all installed applications for a computer.", + "example": "curl http://localhost:5001/api/computers/1/apps -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/computers//apps", + "auth": "permission:computers.create", + "params": "path: computer_id (int); body: JSON (fields not yet documented)", + "purpose": "Add an installed application to a computer.", + "example": "curl -X POST http://localhost:5001/api/computers/1/apps -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/computers//apps/", + "auth": "permission:computers.delete", + "params": "path: computer_id (int), app_id (int)", + "purpose": "Remove an installed application from a computer.", + "example": "curl -X DELETE http://localhost:5001/api/computers/1/apps/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/computers//report", + "auth": "permission:computers.create", + "params": "path: computer_id (int); body: JSON (fields not yet documented)", + "purpose": "Report computer status (for agent-based reporting).", + "example": "curl -X POST http://localhost:5001/api/computers/1/report -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/computers/asset//policypanel", + "auth": "permission:computers.view", + "params": "path: asset_id (int)", + "purpose": "Keyvalue panel: which policy governs this endpoint, and what it allows.", + "example": "curl http://localhost:5001/api/computers/asset/1/policypanel -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/computers/by-asset/", + "auth": "permission:computers.view", + "params": "path: asset_id (int)", + "purpose": "Get computer data by asset ID.", + "example": "curl http://localhost:5001/api/computers/by-asset/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/computers/by-hostname/", + "auth": "permission:computers.view", + "params": "path: hostname (string)", + "purpose": "Get computer by hostname.", + "example": "curl http://localhost:5001/api/computers/by-hostname/x -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/computers/client-script", + "auth": "jwt + role:admin", + "params": "not documented", + "purpose": "The collector reporter, stamped with THIS site's values.", + "example": "curl http://localhost:5001/api/computers/client-script -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/computers/dashboard/quiet", + "auth": "permission:computers.view", + "params": "not documented", + "purpose": "PCs that WERE reporting, have stopped, and are still meant to be in use.", + "example": "curl http://localhost:5001/api/computers/dashboard/quiet -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "POST", - "path": "/api/setup/complete", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Flag the first-run setup as finished.", - "example": "curl -X POST http://localhost:5001/api/setup/complete -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "computerid": 2, + "hostname": "", + "lastreported": "2026-01-01T00:00:00.000000Z", + "quietdays": 9 }, { - "method": "POST", - "path": "/api/setup/create-admin", - "auth": "none (first run only: refused once an admin exists)", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create the first admin. Only works while there are zero users, setup has not been marked complete, and the caller proves operator control (setup token or a loopback request).", - "example": "curl -X POST http://localhost:5001/api/setup/create-admin -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/setup/needs-admin", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "not documented", - "purpose": "True when no users exist yet - the app's very first run. Public so the login screen can offer to create the first admin.", - "example": "curl http://localhost:5001/api/setup/needs-admin" - }, - { - "method": "POST", - "path": "/api/setup/seed-reference", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Seed core reference data + permissions + default settings. Idempotent - reuses the same routines as the `flask seed ...` CLI commands.", - "example": "curl -X POST http://localhost:5001/api/setup/seed-reference -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/setup/seed-starter", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Add common vendors that are not already present. Idempotent.", - "example": "curl -X POST http://localhost:5001/api/setup/seed-starter -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "computerid": 1, + "hostname": "", + "lastreported": "2026-01-01T00:00:00.000000Z", + "quietdays": 2 } - ] - }, - { - "surface": "core-shopdbbackups", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/computers/dashboard/sharedmachines", + "auth": "permission:computers.view", + "params": "not documented", + "purpose": "Machine numbers claimed by more than one PC, with nothing filed under them.", + "example": "curl http://localhost:5001/api/computers/dashboard/sharedmachines -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/shopdbbackups", - "auth": "jwt + role:admin", - "params": "not documented", - "purpose": "List every retained backup set, newest first, with a compatibility flag.", - "example": "curl http://localhost:5001/api/shopdbbackups -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/shopdbbackups", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Kick off an on-demand backup run.", - "example": "curl -X POST http://localhost:5001/api/shopdbbackups -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/shopdbbackups/", - "auth": "jwt + role:admin", - "params": "path: snapshotid (string)", - "purpose": "Return one set's full manifest plus a resolved compatibility verdict.", - "example": "curl http://localhost:5001/api/shopdbbackups/x -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/shopdbbackups//restore", - "auth": "jwt + role:admin", - "params": "path: snapshotid (string); body: JSON (fields not yet documented)", - "purpose": "Queue a restore of a set, gated on schema compatibility.", - "example": "curl -X POST http://localhost:5001/api/shopdbbackups/x/restore -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/shopdbbackups/finalize-restore", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Run the post-restore schema upgrade in the app context.", - "example": "curl -X POST http://localhost:5001/api/shopdbbackups/finalize-restore -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/shopdbbackups/history", - "auth": "jwt + role:admin", - "params": "not documented", - "purpose": "Return the recorded history of backup and restore runs, newest first.", - "example": "curl http://localhost:5001/api/shopdbbackups/history -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/shopdbbackups/settings", - "auth": "jwt + role:admin", - "params": "not documented", - "purpose": "Return destination + retention config for the settings pane.", - "example": "curl http://localhost:5001/api/shopdbbackups/settings -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/shopdbbackups/settings", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Persist the backup destination kind and its credential/config fields.", - "example": "curl -X PUT http://localhost:5001/api/shopdbbackups/settings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/shopdbbackups/status", - "auth": "jwt + role:admin", - "params": "not documented", - "purpose": "Return the live status of the most recent backup and restore runs.", - "example": "curl http://localhost:5001/api/shopdbbackups/status -H \"Authorization: Bearer $TOK\"" + "assetid": 1, + "machinenumber": "2026", + "pccount": 2, + "pcs": "PCONE, PCTWO" } - ] - }, - { - "surface": "core-supportteams", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/computers/dashboard/summary", + "auth": "permission:computers.view", + "params": "not documented", + "purpose": "Get computer dashboard summary data.", + "example": "curl http://localhost:5001/api/computers/dashboard/summary -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "byos": [], + "bytype": [], + "nonshopfloor": 0, + "shopfloor": 0, + "total": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/computers/display-kiosks", + "auth": "permission:computers.view", + "params": "not documented", + "purpose": "Reporting display kiosks, for the Dashboard Defaults picker.", + "example": "curl http://localhost:5001/api/computers/display-kiosks -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/supportteams", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "List support teams (with contacts). ?active and ?teamname filters.", - "example": "curl http://localhost:5001/api/supportteams -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/supportteams", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new support team.", - "example": "curl -X POST http://localhost:5001/api/supportteams -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/supportteams/", - "auth": "jwt + role:admin", - "params": "path: team_id (int)", - "purpose": "Delete a support team; 409 while any application references it.", - "example": "curl -X DELETE http://localhost:5001/api/supportteams/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/supportteams/", - "auth": "permission:assets.view", - "params": "path: team_id (int)", - "purpose": "Get a single support team with its contacts.", - "example": "curl http://localhost:5001/api/supportteams/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/supportteams/", - "auth": "jwt + role:admin", - "params": "path: team_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a support team.", - "example": "curl -X PUT http://localhost:5001/api/supportteams/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/supportteams//contacts", - "auth": "jwt + role:admin", - "params": "path: team_id (int); body: JSON (fields not yet documented)", - "purpose": "Add a contact to a support team.", - "example": "curl -X POST http://localhost:5001/api/supportteams/1/contacts -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/supportteams//contacts/", - "auth": "jwt + role:admin", - "params": "path: team_id (int), contact_id (int)", - "purpose": "Delete a support-team contact.", - "example": "curl -X DELETE http://localhost:5001/api/supportteams/1/contacts/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/supportteams//contacts/", - "auth": "jwt + role:admin", - "params": "path: team_id (int), contact_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a support-team contact.", - "example": "curl -X PUT http://localhost:5001/api/supportteams/1/contacts/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "computerid": 1, + "fqdn": "", + "hostname": "", + "serialnumber": "" } - ] - }, - { - "surface": "core-users", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/computers/policies/actions", + "auth": "permission:computers.view", + "params": "not documented", + "purpose": "The allowlisted action names + labels a policy may reference.", + "example": "curl http://localhost:5001/api/computers/policies/actions -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/users", - "auth": "jwt + role:admin", - "params": "not documented", - "purpose": "List all users.", - "example": "curl http://localhost:5001/api/users -H \"Authorization: Bearer $TOK\"" + "action": "service_start", + "description": "Start a Windows service by name on the endpoint.", + "kind": "command", + "label": "Start service", + "mayauthor": true, + "requiredpermission": null }, { - "method": "POST", - "path": "/api/users", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new user.", - "example": "curl -X POST http://localhost:5001/api/users -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "action": "service_stop", + "description": "Stop a running Windows service by name on the endpoint.", + "kind": "command", + "label": "Stop service", + "mayauthor": true, + "requiredpermission": null }, { - "method": "DELETE", - "path": "/api/users/", - "auth": "jwt + role:admin", - "params": "path: userid (int)", - "purpose": "Delete a user.", - "example": "curl -X DELETE http://localhost:5001/api/users/1 -H \"Authorization: Bearer $TOK\"" + "action": "service_status", + "description": "Report whether a named Windows service is running.", + "kind": "command", + "label": "Check service", + "mayauthor": true, + "requiredpermission": null }, + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/computers/policies/business", + "auth": "permission:computers.view", + "params": "not documented", + "purpose": "UNDOCUMENTED: computers.list_business_policies has no docstring.", + "example": "curl http://localhost:5001/api/computers/policies/business -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/computers/policies/business", + "auth": "permission:computers.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: computers.create_business_policy has no docstring.", + "example": "curl -X POST http://localhost:5001/api/computers/policies/business -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/computers/policies/business/", + "auth": "permission:computers.delete", + "params": "path: policy_id (int)", + "purpose": "UNDOCUMENTED: computers.delete_business_policy has no docstring.", + "example": "curl -X DELETE http://localhost:5001/api/computers/policies/business/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/computers/policies/business/", + "auth": "permission:computers.view", + "params": "path: policy_id (int)", + "purpose": "UNDOCUMENTED: computers.get_business_policy has no docstring.", + "example": "curl http://localhost:5001/api/computers/policies/business/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/computers/policies/business/", + "auth": "permission:computers.edit", + "params": "path: policy_id (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: computers.update_business_policy has no docstring.", + "example": "curl -X PUT http://localhost:5001/api/computers/policies/business/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/computers/policies/configuration", + "auth": "permission:computers.view", + "params": "not documented", + "purpose": "UNDOCUMENTED: computers.list_configuration_policies has no docstring.", + "example": "curl http://localhost:5001/api/computers/policies/configuration -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/computers/policies/configuration", + "auth": "permission:computers.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: computers.create_configuration_policy has no docstring.", + "example": "curl -X POST http://localhost:5001/api/computers/policies/configuration -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "actions": [ { - "method": "GET", - "path": "/api/users/", - "auth": "jwt", - "params": "path: userid (int)", - "purpose": "Get a single user.", - "example": "curl http://localhost:5001/api/users/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/users/", - "auth": "jwt", - "params": "path: userid (int); body: JSON (fields not yet documented)", - "purpose": "Update a user.", - "example": "curl -X PUT http://localhost:5001/api/users/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/users/permissions", - "auth": "jwt", - "params": "not documented", - "purpose": "List assignable permissions grouped by category.", - "example": "curl http://localhost:5001/api/users/permissions -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/users/roles", - "auth": "jwt", - "params": "not documented", - "purpose": "List all roles with their permissions.", - "example": "curl http://localhost:5001/api/users/roles -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/users/roles", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new role.", - "example": "curl -X POST http://localhost:5001/api/users/roles -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/users/roles/", - "auth": "jwt + role:admin", - "params": "path: roleid (int)", - "purpose": "Delete a role.", - "example": "curl -X DELETE http://localhost:5001/api/users/roles/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/users/roles/", - "auth": "jwt + role:admin", - "params": "path: roleid (int); body: JSON (fields not yet documented)", - "purpose": "Update a role.", - "example": "curl -X PUT http://localhost:5001/api/users/roles/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "action": "run_script" } + ], + "name": "Legitimate" + }, + "responseexample": { + "data": { + "actions": [ + { + "action": "run_script", + "configurationpolicyactionid": 1, + "configurationpolicyid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "desiredstate": null, + "intervalminutes": null, + "isactive": true, + "mode": "gate", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "params": null, + "sortorder": 0 + } + ], + "configurationpolicyid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Legitimate", + "scopejson": null, + "source": "local" + }, + "message": "Configuration policy created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/computers/policies/configuration/", + "auth": "permission:computers.delete", + "params": "path: policy_id (int)", + "purpose": "UNDOCUMENTED: computers.delete_configuration_policy has no docstring.", + "example": "curl -X DELETE http://localhost:5001/api/computers/policies/configuration/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/computers/policies/configuration/", + "auth": "permission:computers.view", + "params": "path: policy_id (int)", + "purpose": "UNDOCUMENTED: computers.get_configuration_policy has no docstring.", + "example": "curl http://localhost:5001/api/computers/policies/configuration/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/computers/policies/configuration/", + "auth": "permission:computers.edit", + "params": "path: policy_id (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: computers.update_configuration_policy has no docstring.", + "example": "curl -X PUT http://localhost:5001/api/computers/policies/configuration/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "description": "clearer wording", + "name": "New Name" + }, + "responseexample": { + "data": { + "actions": [ + { + "action": "run_script", + "configurationpolicyactionid": 1, + "configurationpolicyid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "desiredstate": null, + "intervalminutes": null, + "isactive": true, + "mode": "gate", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "params": null, + "sortorder": 0 + } + ], + "configurationpolicyid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "clearer wording", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "New Name", + "scopejson": null, + "source": "local" + }, + "message": "Configuration policy updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/computers/policies/effective/", + "auth": "permission:computers.view", + "params": "path: asset_id (int)", + "purpose": "Preview the resolved effective policy for one endpoint (debug/UI).", + "example": "curl http://localhost:5001/api/computers/policies/effective/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/computers/protocols", + "auth": "permission:computers.view", + "params": "not documented", + "purpose": "List access protocols. ?active=false includes disabled ones.", + "example": "curl http://localhost:5001/api/computers/protocols -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + } + ], + "responseexample": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/computers/protocols", + "auth": "permission:computers.edit", + "params": "body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: computers.create_protocol has no docstring.", + "example": "curl -X POST http://localhost:5001/api/computers/protocols -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/computers/protocols/", + "auth": "permission:computers.edit", + "params": "path: protocol_id (int)", + "purpose": "UNDOCUMENTED: computers.delete_protocol has no docstring.", + "example": "curl -X DELETE http://localhost:5001/api/computers/protocols/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PATCH|PUT", + "path": "/api/computers/protocols/", + "auth": "permission:computers.edit", + "params": "path: protocol_id (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: computers.update_protocol has no docstring.", + "example": "curl -X PATCH http://localhost:5001/api/computers/protocols/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/computers/types", + "auth": "permission:computers.view", + "params": "not documented", + "purpose": "List all computer types.", + "example": "curl http://localhost:5001/api/computers/types -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/computers/types", + "auth": "permission:computers.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new computer type.", + "example": "curl -X POST http://localhost:5001/api/computers/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/computers/types/", + "auth": "permission:computers.delete", + "params": "path: type_id (int)", + "purpose": "Delete a computer type. Refused if any PC still uses it.", + "example": "curl -X DELETE http://localhost:5001/api/computers/types/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/computers/types/", + "auth": "permission:computers.view", + "params": "path: type_id (int)", + "purpose": "Get a single computer type.", + "example": "curl http://localhost:5001/api/computers/types/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/computers/types/", + "auth": "permission:computers.edit", + "params": "path: type_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a computer type.", + "example": "curl -X PUT http://localhost:5001/api/computers/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + } + ] + }, + { + "surface": "plugin-employees", + "endpoints": [ + { + "method": "DELETE", + "path": "/api/employees//photo", + "auth": "jwt + role:admin", + "params": "path: sso (int)", + "purpose": "Clear an employee photo and delete the uploaded file. External mode 409s.", + "example": "curl -X DELETE http://localhost:5001/api/employees/1/photo -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "First_Name": "Test", + "Last_Name": "Person", + "Picture": null, + "Role": null, + "SSO": 210000001, + "Team": null, + "photofilename": null, + "photourl": null + }, + "message": "Employee photo removed", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/employees//photo", + "auth": "jwt + role:admin", + "params": "path: sso (int); body: JSON (fields not yet documented)", + "purpose": "Upload (or replace) the photo for a self-hosted directory employee.", + "example": "curl -X POST http://localhost:5001/api/employees/1/photo -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "First_Name": "Test", + "Last_Name": "Person", + "Picture": null, + "Role": null, + "SSO": 210000001, + "Team": null, + "photofilename": "photo-210000001.jpg", + "photourl": "/api/employees/photo/photo-210000001.jpg" + }, + "message": "Employee photo uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/employees/directory", + "auth": "permission:employees.view", + "params": "not documented", + "purpose": "Full self-hosted directory (for the management page).", + "example": "curl http://localhost:5001/api/employees/directory -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/employees/directory", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: employees.create_directory_employee has no docstring.", + "example": "curl -X POST http://localhost:5001/api/employees/directory -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/employees/directory/", + "auth": "jwt + role:admin", + "params": "path: sso (int)", + "purpose": "UNDOCUMENTED: employees.delete_directory_employee has no docstring.", + "example": "curl -X DELETE http://localhost:5001/api/employees/directory/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/employees/directory/", + "auth": "jwt + role:admin", + "params": "path: sso (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: employees.update_directory_employee has no docstring.", + "example": "curl -X PUT http://localhost:5001/api/employees/directory/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "POST", + "path": "/api/employees/directory/import", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Bulk upsert from CSV. Accepts headers SSO,First_Name,Last_Name,Team,Role, Picture (case-insensitive; sso/firstname/... also accepted).", + "example": "curl -X POST http://localhost:5001/api/employees/directory/import -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/employees/lookup", + "auth": "permission:employees.view", + "params": "not documented", + "purpose": "Look up multiple employees by SSO list.", + "example": "curl http://localhost:5001/api/employees/lookup -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "sso", + "type": "string", + "required": false, + "default": "" + } ] - }, - { - "surface": "core-vendors", - "endpoints": [ + }, + { + "method": "GET", + "path": "/api/employees/lookup/", + "auth": "permission:employees.view", + "params": "path: sso (string)", + "purpose": "Look up a single employee by SSO.", + "example": "curl http://localhost:5001/api/employees/lookup/x -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "First_Name": "Test", + "Last_Name": "Person", + "Picture": null, + "Role": null, + "SSO": 210000001, + "Team": null, + "photofilename": "photo-210000001.png", + "photourl": "/api/employees/photo/photo-210000001.png" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/employees/photo/", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "path: filename (path)", + "purpose": "Serve an uploaded employee photo (public - kiosk cards read it).", + "example": "curl http://localhost:5001/api/employees/photo/example.png" + }, + { + "method": "GET", + "path": "/api/employees/resolve/", + "auth": "permission:employees.view", + "params": "path: sso (string)", + "purpose": "Resolve one SSO to a name and photo, and say HOW.", + "example": "curl http://localhost:5001/api/employees/resolve/x -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "error": "No name found in the directory or in a user account", + "mode": "selfhosted", + "name": null, + "photourl": null, + "source": null, + "sso": "999999" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/employees/search", + "auth": "permission:employees.view", + "params": "not documented", + "purpose": "Search employees by name.", + "example": "curl http://localhost:5001/api/employees/search -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "limit", + "type": "integer", + "required": false, + "default": 10 + }, + { + "name": "q", + "type": "string", + "required": false, + "default": "" + } + ], + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/vendors", - "auth": "permission:assets.view", - "params": "not documented", - "purpose": "List all vendors.", - "example": "curl http://localhost:5001/api/vendors -H \"Authorization: Bearer $TOK\"" + "First_Name": "Dana", + "Last_Name": "Reyes", + "Picture": null, + "Role": null, + "SSO": 990005, + "Team": null, + "photofilename": null, + "photourl": null }, { - "method": "POST", - "path": "/api/vendors", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new vendor.", - "example": "curl -X POST http://localhost:5001/api/vendors -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/vendors/", - "auth": "jwt + role:admin", - "params": "path: vendor_id (int)", - "purpose": "Delete (deactivate) a vendor.", - "example": "curl -X DELETE http://localhost:5001/api/vendors/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/vendors/", - "auth": "permission:assets.view", - "params": "path: vendor_id (int)", - "purpose": "Get a single vendor.", - "example": "curl http://localhost:5001/api/vendors/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/vendors/", - "auth": "jwt + role:admin", - "params": "path: vendor_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a vendor.", - "example": "curl -X PUT http://localhost:5001/api/vendors/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "First_Name": "Sam", + "Last_Name": "Reyes", + "Picture": null, + "Role": null, + "SSO": 990006, + "Team": null, + "photofilename": null, + "photourl": null } - ] - }, - { - "surface": "plugin-backups", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + } + ] + }, + { + "surface": "plugin-endpoints", + "endpoints": [ + { + "method": "GET", + "path": "/api/endpoints", + "auth": "permission:endpoints.fleet", + "params": "not documented", + "purpose": "List reported endpoint states.", + "example": "curl http://localhost:5001/api/endpoints -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "businessunitid", + "type": "string", + "required": false + }, + { + "name": "computertypeid", + "type": "string", + "required": false + }, + { + "name": "credentialstate", + "type": "string", + "required": false + }, + { + "name": "locationid", + "type": "string", + "required": false + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "sort", + "type": "string", + "required": false + }, + { + "name": "status", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/backups/asset/", - "auth": "permission:backups.view", - "params": "path: assetid (int)", - "purpose": "Revision history for one asset, newest first.", - "example": "curl http://localhost:5001/api/backups/asset/1 -H \"Authorization: Bearer $TOK\"" + "agentversion": "1.20.0", + "alias": "REVOKED", + "assetid": 6, + "assetnumber": "REVOKED", + "computerid": 6, + "computertype": null, + "computertypeid": null, + "cpupercent": null, + "createdat": "2026-01-01T00:00:00.000000Z", + "credentialstate": "", + "domain": null, + "endpointstateid": 6, + "health": { + "label": "ok", + "level": "ok" + }, + "hostname": "", + "idleseconds": null, + "lastboottime": null, + "lastreportedat": "2026-01-01T00:00:00.000000Z", + "loggedinuser": null, + "machines": [], + "memorypercent": null, + "mtconnectcheckedat": null, + "mtconnectrunning": null, + "pendingreboot": null, + "status": "reporting", + "trayversion": null, + "uptimedisplay": null, + "uptimeseconds": null }, { - "method": "GET", - "path": "/api/backups/asset//info", - "auth": "permission:backups.view", - "params": "path: assetid (int)", - "purpose": "A kind's 'at a glance' card, built from its LATEST revision.", - "example": "curl http://localhost:5001/api/backups/asset/1/info -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/backups/dashboard/stale", - "auth": "permission:backups.view", - "params": "not documented", - "purpose": "Chains whose backup has stopped running.", - "example": "curl http://localhost:5001/api/backups/dashboard/stale -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/backups/kinds", - "auth": "permission:backups.view", - "params": "not documented", - "purpose": "Registered backup kinds, for UI that needs to enumerate them.", - "example": "curl http://localhost:5001/api/backups/kinds -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/backups/revisions/", - "auth": "permission:backups.view", - "params": "path: backuprevisionid (int)", - "purpose": "One revision, including its parsed payload where there is one.", - "example": "curl http://localhost:5001/api/backups/revisions/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/backups/revisions//diff", - "auth": "permission:backups.view", - "params": "path: backuprevisionid (int)", - "purpose": "Diff two revisions of the same asset+kind.", - "example": "curl http://localhost:5001/api/backups/revisions/1/diff -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/backups/revisions//download", - "auth": "permission:backups.download", - "params": "path: backuprevisionid (int)", - "purpose": "Render a revision back to its native file format.", - "example": "curl http://localhost:5001/api/backups/revisions/1/download -H \"Authorization: Bearer $TOK\"" + "agentversion": "1.20.0", + "alias": "SHARED", + "assetid": 5, + "assetnumber": "SHARED", + "computerid": 5, + "computertype": null, + "computertypeid": null, + "cpupercent": null, + "createdat": "2026-01-01T00:00:00.000000Z", + "credentialstate": "", + "domain": null, + "endpointstateid": 5, + "health": { + "label": "ok", + "level": "ok" + }, + "hostname": "", + "idleseconds": null, + "lastboottime": null, + "lastreportedat": "2026-01-01T00:00:00.000000Z", + "loggedinuser": null, + "machines": [], + "memorypercent": null, + "mtconnectcheckedat": null, + "mtconnectrunning": null, + "pendingreboot": null, + "status": "reporting", + "trayversion": null, + "uptimedisplay": null, + "uptimeseconds": null } - ] - }, - { - "surface": "plugin-computers", - "endpoints": [ - { - "method": "GET", - "path": "/api/computers", - "auth": "permission:computers.view", - "params": "not documented", - "purpose": "List all computers with filtering and pagination.", - "example": "curl http://localhost:5001/api/computers -H \"Authorization: Bearer $TOK\"" + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 2, + "totalpages": 1 }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/endpoints/", + "auth": "permission:endpoints.fleet", + "params": "not documented", + "purpose": "List reported endpoint states.", + "example": "curl http://localhost:5001/api/endpoints/ -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "businessunitid", + "type": "string", + "required": false + }, + { + "name": "computertypeid", + "type": "string", + "required": false + }, + { + "name": "credentialstate", + "type": "string", + "required": false + }, + { + "name": "locationid", + "type": "string", + "required": false + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "sort", + "type": "string", + "required": false + }, + { + "name": "status", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [ { - "method": "POST", - "path": "/api/computers", - "auth": "permission:computers.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create new computer (creates both Asset and Computer records).", - "example": "curl -X POST http://localhost:5001/api/computers -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/computers/", - "auth": "permission:computers.delete", - "params": "path: computer_id (int)", - "purpose": "Delete (soft delete) computer.", - "example": "curl -X DELETE http://localhost:5001/api/computers/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/computers/", - "auth": "permission:computers.view", - "params": "path: computer_id (int)", - "purpose": "Get a single computer with full details.", - "example": "curl http://localhost:5001/api/computers/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/computers/", - "auth": "permission:computers.edit", - "params": "path: computer_id (int); body: JSON (fields not yet documented)", - "purpose": "Update computer (both Asset and Computer records).", - "example": "curl -X PUT http://localhost:5001/api/computers/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/computers//apps", - "auth": "permission:computers.view", - "params": "path: computer_id (int)", - "purpose": "Get all installed applications for a computer.", - "example": "curl http://localhost:5001/api/computers/1/apps -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/computers//apps", - "auth": "permission:computers.create", - "params": "path: computer_id (int); body: JSON (fields not yet documented)", - "purpose": "Add an installed application to a computer.", - "example": "curl -X POST http://localhost:5001/api/computers/1/apps -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/computers//apps/", - "auth": "permission:computers.delete", - "params": "path: computer_id (int), app_id (int)", - "purpose": "Remove an installed application from a computer.", - "example": "curl -X DELETE http://localhost:5001/api/computers/1/apps/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/computers//report", - "auth": "permission:computers.create", - "params": "path: computer_id (int); body: JSON (fields not yet documented)", - "purpose": "Report computer status (for agent-based reporting).", - "example": "curl -X POST http://localhost:5001/api/computers/1/report -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/computers/asset//policypanel", - "auth": "permission:computers.view", - "params": "path: asset_id (int)", - "purpose": "Keyvalue panel: which policy governs this endpoint, and what it allows.", - "example": "curl http://localhost:5001/api/computers/asset/1/policypanel -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/computers/by-asset/", - "auth": "permission:computers.view", - "params": "path: asset_id (int)", - "purpose": "Get computer data by asset ID.", - "example": "curl http://localhost:5001/api/computers/by-asset/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/computers/by-hostname/", - "auth": "permission:computers.view", - "params": "path: hostname (string)", - "purpose": "Get computer by hostname.", - "example": "curl http://localhost:5001/api/computers/by-hostname/x -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/computers/client-script", - "auth": "jwt + role:admin", - "params": "not documented", - "purpose": "The collector reporter, stamped with THIS site's values.", - "example": "curl http://localhost:5001/api/computers/client-script -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/computers/dashboard/quiet", - "auth": "permission:computers.view", - "params": "not documented", - "purpose": "PCs that WERE reporting, have stopped, and are still meant to be in use.", - "example": "curl http://localhost:5001/api/computers/dashboard/quiet -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/computers/dashboard/sharedmachines", - "auth": "permission:computers.view", - "params": "not documented", - "purpose": "Machine numbers claimed by more than one PC, with nothing filed under them.", - "example": "curl http://localhost:5001/api/computers/dashboard/sharedmachines -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/computers/dashboard/summary", - "auth": "permission:computers.view", - "params": "not documented", - "purpose": "Get computer dashboard summary data.", - "example": "curl http://localhost:5001/api/computers/dashboard/summary -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/computers/display-kiosks", - "auth": "permission:computers.view", - "params": "not documented", - "purpose": "Reporting display kiosks, for the Dashboard Defaults picker.", - "example": "curl http://localhost:5001/api/computers/display-kiosks -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/computers/policies/actions", - "auth": "permission:computers.view", - "params": "not documented", - "purpose": "The allowlisted action names + labels a policy may reference.", - "example": "curl http://localhost:5001/api/computers/policies/actions -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/computers/policies/business", - "auth": "permission:computers.view", - "params": "not documented", - "purpose": "UNDOCUMENTED: computers.list_business_policies has no docstring.", - "example": "curl http://localhost:5001/api/computers/policies/business -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/computers/policies/business", - "auth": "permission:computers.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: computers.create_business_policy has no docstring.", - "example": "curl -X POST http://localhost:5001/api/computers/policies/business -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/computers/policies/business/", - "auth": "permission:computers.delete", - "params": "path: policy_id (int)", - "purpose": "UNDOCUMENTED: computers.delete_business_policy has no docstring.", - "example": "curl -X DELETE http://localhost:5001/api/computers/policies/business/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/computers/policies/business/", - "auth": "permission:computers.view", - "params": "path: policy_id (int)", - "purpose": "UNDOCUMENTED: computers.get_business_policy has no docstring.", - "example": "curl http://localhost:5001/api/computers/policies/business/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/computers/policies/business/", - "auth": "permission:computers.edit", - "params": "path: policy_id (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: computers.update_business_policy has no docstring.", - "example": "curl -X PUT http://localhost:5001/api/computers/policies/business/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/computers/policies/configuration", - "auth": "permission:computers.view", - "params": "not documented", - "purpose": "UNDOCUMENTED: computers.list_configuration_policies has no docstring.", - "example": "curl http://localhost:5001/api/computers/policies/configuration -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/computers/policies/configuration", - "auth": "permission:computers.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: computers.create_configuration_policy has no docstring.", - "example": "curl -X POST http://localhost:5001/api/computers/policies/configuration -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/computers/policies/configuration/", - "auth": "permission:computers.delete", - "params": "path: policy_id (int)", - "purpose": "UNDOCUMENTED: computers.delete_configuration_policy has no docstring.", - "example": "curl -X DELETE http://localhost:5001/api/computers/policies/configuration/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/computers/policies/configuration/", - "auth": "permission:computers.view", - "params": "path: policy_id (int)", - "purpose": "UNDOCUMENTED: computers.get_configuration_policy has no docstring.", - "example": "curl http://localhost:5001/api/computers/policies/configuration/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/computers/policies/configuration/", - "auth": "permission:computers.edit", - "params": "path: policy_id (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: computers.update_configuration_policy has no docstring.", - "example": "curl -X PUT http://localhost:5001/api/computers/policies/configuration/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/computers/policies/effective/", - "auth": "permission:computers.view", - "params": "path: asset_id (int)", - "purpose": "Preview the resolved effective policy for one endpoint (debug/UI).", - "example": "curl http://localhost:5001/api/computers/policies/effective/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/computers/protocols", - "auth": "permission:computers.view", - "params": "not documented", - "purpose": "List access protocols. ?active=false includes disabled ones.", - "example": "curl http://localhost:5001/api/computers/protocols -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/computers/protocols", - "auth": "permission:computers.edit", - "params": "body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: computers.create_protocol has no docstring.", - "example": "curl -X POST http://localhost:5001/api/computers/protocols -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/computers/protocols/", - "auth": "permission:computers.edit", - "params": "path: protocol_id (int)", - "purpose": "UNDOCUMENTED: computers.delete_protocol has no docstring.", - "example": "curl -X DELETE http://localhost:5001/api/computers/protocols/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PATCH|PUT", - "path": "/api/computers/protocols/", - "auth": "permission:computers.edit", - "params": "path: protocol_id (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: computers.update_protocol has no docstring.", - "example": "curl -X PATCH http://localhost:5001/api/computers/protocols/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/computers/types", - "auth": "permission:computers.view", - "params": "not documented", - "purpose": "List all computer types.", - "example": "curl http://localhost:5001/api/computers/types -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/computers/types", - "auth": "permission:computers.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new computer type.", - "example": "curl -X POST http://localhost:5001/api/computers/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/computers/types/", - "auth": "permission:computers.delete", - "params": "path: type_id (int)", - "purpose": "Delete a computer type. Refused if any PC still uses it.", - "example": "curl -X DELETE http://localhost:5001/api/computers/types/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/computers/types/", - "auth": "permission:computers.view", - "params": "path: type_id (int)", - "purpose": "Get a single computer type.", - "example": "curl http://localhost:5001/api/computers/types/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/computers/types/", - "auth": "permission:computers.edit", - "params": "path: type_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a computer type.", - "example": "curl -X PUT http://localhost:5001/api/computers/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "agentversion": null, + "alias": "Bay 3 Controller", + "assetid": 1, + "assetnumber": "fleetbox", + "computerid": 1, + "computertype": null, + "computertypeid": null, + "cpupercent": null, + "createdat": "2026-01-01T00:00:00.000000Z", + "credentialstate": "", + "domain": null, + "endpointstateid": 1, + "health": { + "label": "no agent", + "level": "unknown" + }, + "hostname": "", + "idleseconds": null, + "lastboottime": null, + "lastreportedat": null, + "loggedinuser": null, + "machines": [], + "memorypercent": null, + "mtconnectcheckedat": null, + "mtconnectrunning": null, + "pendingreboot": null, + "status": "never", + "trayversion": null, + "uptimedisplay": null, + "uptimeseconds": null } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/endpoints/", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "Get the current state for one endpoint by asset id.", + "example": "curl http://localhost:5001/api/endpoints/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "actionminimums": { + "collect_log": "1.16.0" + }, + "agentcurrent": "1.6.3", + "agentversion": "1.20.0", + "assetid": 2, + "cpupercent": null, + "createdat": "2026-01-01T00:00:00.000000Z", + "credential": { + "credentialprefix": "", + "enrolledvia": "auto", + "issuedat": "2026-01-01T00:00:00.000000Z", + "lastusedat": "2026-01-01T00:00:00.000000Z", + "reenrollblocked": true, + "reenrollblockedreason": "rollback", + "revokedat": "2026-01-01T00:00:00.000000Z", + "state": "reenrollblocked", + "statelabel": "Re-enroll blocked" + }, + "domain": null, + "endpointstateid": 2, + "hostname": "", + "idleseconds": null, + "lastboottime": null, + "lastreportedat": "2026-01-01T00:00:00.000000Z", + "loggedinuser": null, + "memorypercent": null, + "mtconnectcheckedat": null, + "mtconnectrunning": null, + "pendingreboot": null, + "status": "reporting", + "trayversion": null, + "uptimedisplay": null, + "uptimeseconds": null + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/endpoints//agentupgrade", + "auth": "permission:endpoints.agentupdate", + "params": "path: asset_id (int); body: JSON (fields not yet documented)", + "purpose": "Push the published agent build to one endpoint.", + "example": "curl -X POST http://localhost:5001/api/endpoints/1/agentupgrade -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "action": "run_script", + "target": "script:1" + }, + "responseexample": { + "data": { + "action": "run_script", + "assetid": 1, + "claimedat": null, + "completedat": null, + "createdat": "2026-01-01T00:00:00.000000Z", + "endpointcommandid": 1, + "requestedby": "", + "resultmessage": null, + "status": "pending", + "target": "script:1", + "targetkind": "script" + }, + "message": "Command queued", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/endpoints//commands", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "Recent commands for one endpoint, newest first (operator view).", + "example": "curl http://localhost:5001/api/endpoints/1/commands -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/endpoints//commands", + "auth": "permission:endpoints.command", + "params": "path: asset_id (int); body: JSON (fields not yet documented)", + "purpose": "Queue an allowlisted command for one endpoint.", + "example": "curl -X POST http://localhost:5001/api/endpoints/1/commands -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "action": "service_status", + "overridereason": "line down, need status", + "policyoverride": true, + "target": "spooler" + }, + "responseexample": { + "data": { + "action": "collect_log", + "assetid": 1, + "claimedat": null, + "completedat": null, + "createdat": "2026-01-01T00:00:00.000000Z", + "endpointcommandid": 1, + "requestedby": "", + "resultmessage": null, + "status": "pending", + "target": "C:\\logs\\app.log", + "targetkind": "logpath" + }, + "message": "Command queued", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/endpoints//events", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "List curated Windows events for one endpoint, newest first.", + "example": "curl http://localhost:5001/api/endpoints/1/events -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "eventid", + "type": "string", + "required": false + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + } ] - }, - { - "surface": "plugin-employees", - "endpoints": [ + }, + { + "method": "GET", + "path": "/api/endpoints//holdableactions", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "Desired-mode actions on this endpoint, with current hold state.", + "example": "curl http://localhost:5001/api/endpoints/1/holdableactions -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "DELETE", - "path": "/api/employees//photo", - "auth": "jwt + role:admin", - "params": "path: sso (int)", - "purpose": "Clear an employee photo and delete the uploaded file. External mode 409s.", - "example": "curl -X DELETE http://localhost:5001/api/employees/1/photo -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/employees//photo", - "auth": "jwt + role:admin", - "params": "path: sso (int); body: JSON (fields not yet documented)", - "purpose": "Upload (or replace) the photo for a self-hosted directory employee.", - "example": "curl -X POST http://localhost:5001/api/employees/1/photo -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/employees/directory", - "auth": "permission:employees.view", - "params": "not documented", - "purpose": "Full self-hosted directory (for the management page).", - "example": "curl http://localhost:5001/api/employees/directory -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/employees/directory", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: employees.create_directory_employee has no docstring.", - "example": "curl -X POST http://localhost:5001/api/employees/directory -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/employees/directory/", - "auth": "jwt + role:admin", - "params": "path: sso (int)", - "purpose": "UNDOCUMENTED: employees.delete_directory_employee has no docstring.", - "example": "curl -X DELETE http://localhost:5001/api/employees/directory/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/employees/directory/", - "auth": "jwt + role:admin", - "params": "path: sso (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: employees.update_directory_employee has no docstring.", - "example": "curl -X PUT http://localhost:5001/api/employees/directory/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/employees/directory/import", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Bulk upsert from CSV. Accepts headers SSO,First_Name,Last_Name,Team,Role, Picture (case-insensitive; sso/firstname/... also accepted).", - "example": "curl -X POST http://localhost:5001/api/employees/directory/import -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/employees/lookup", - "auth": "permission:employees.view", - "params": "not documented", - "purpose": "Look up multiple employees by SSO list.", - "example": "curl http://localhost:5001/api/employees/lookup -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/employees/lookup/", - "auth": "permission:employees.view", - "params": "path: sso (string)", - "purpose": "Look up a single employee by SSO.", - "example": "curl http://localhost:5001/api/employees/lookup/x -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/employees/photo/", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "path: filename (path)", - "purpose": "Serve an uploaded employee photo (public - kiosk cards read it).", - "example": "curl http://localhost:5001/api/employees/photo/example.png" - }, - { - "method": "GET", - "path": "/api/employees/resolve/", - "auth": "permission:employees.view", - "params": "path: sso (string)", - "purpose": "Resolve one SSO to a name and photo, and say HOW.", - "example": "curl http://localhost:5001/api/employees/resolve/x -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/employees/search", - "auth": "permission:employees.view", - "params": "not documented", - "purpose": "Search employees by name.", - "example": "curl http://localhost:5001/api/employees/search -H \"Authorization: Bearer $TOK\"" + "action": "service_start", + "held": false } - ] - }, - { - "surface": "plugin-endpoints", - "endpoints": [ - { - "method": "GET", - "path": "/api/endpoints", - "auth": "permission:endpoints.fleet", - "params": "not documented", - "purpose": "List reported endpoint states.", - "example": "curl http://localhost:5001/api/endpoints -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/", - "auth": "permission:endpoints.fleet", - "params": "not documented", - "purpose": "List reported endpoint states.", - "example": "curl http://localhost:5001/api/endpoints/ -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "Get the current state for one endpoint by asset id.", - "example": "curl http://localhost:5001/api/endpoints/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/endpoints//agentupgrade", - "auth": "permission:endpoints.agentupdate", - "params": "path: asset_id (int); body: JSON (fields not yet documented)", - "purpose": "Push the published agent build to one endpoint.", - "example": "curl -X POST http://localhost:5001/api/endpoints/1/agentupgrade -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/endpoints//commands", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "Recent commands for one endpoint, newest first (operator view).", - "example": "curl http://localhost:5001/api/endpoints/1/commands -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/endpoints//commands", - "auth": "permission:endpoints.command", - "params": "path: asset_id (int); body: JSON (fields not yet documented)", - "purpose": "Queue an allowlisted command for one endpoint.", - "example": "curl -X POST http://localhost:5001/api/endpoints/1/commands -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/endpoints//events", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "List curated Windows events for one endpoint, newest first.", - "example": "curl http://localhost:5001/api/endpoints/1/events -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints//holdableactions", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "Desired-mode actions on this endpoint, with current hold state.", - "example": "curl http://localhost:5001/api/endpoints/1/holdableactions -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "DELETE", - "path": "/api/endpoints//lock", - "auth": "permission:endpoints.command", - "params": "path: asset_id (int)", - "purpose": "Lift the quarantine on this endpoint.", - "example": "curl -X DELETE http://localhost:5001/api/endpoints/1/lock -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints//lock", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "The endpoint lock standing on this endpoint, or null.", - "example": "curl http://localhost:5001/api/endpoints/1/lock -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/endpoints//lock", - "auth": "permission:endpoints.command", - "params": "path: asset_id (int); body: JSON (fields not yet documented)", - "purpose": "Quarantine this endpoint against non-admin commands.", - "example": "curl -X POST http://localhost:5001/api/endpoints/1/lock -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/endpoints//logcollections", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "Collected-log history for one endpoint, newest first.", - "example": "curl http://localhost:5001/api/endpoints/1/logcollections -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints//policyholds", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "Active break-glass holds for one endpoint.", - "example": "curl http://localhost:5001/api/endpoints/1/policyholds -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/endpoints//policyholds", - "auth": "permission:endpoints.command", - "params": "path: asset_id (int); body: JSON (fields not yet documented)", - "purpose": "Deliberately suspend desired-state reconciliation for one or more actions.", - "example": "curl -X POST http://localhost:5001/api/endpoints/1/policyholds -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/endpoints//policyholds/", - "auth": "permission:endpoints.command", - "params": "path: asset_id (int), action (string)", - "purpose": "End a hold early, resuming reconciliation of one action.", - "example": "curl -X DELETE http://localhost:5001/api/endpoints/1/policyholds/x -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints//scriptruns", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "The last few run_script commands for one endpoint, newest first.", - "example": "curl http://localhost:5001/api/endpoints/1/scriptruns -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/endpoints/adoptiontool/package", - "auth": "permission:endpoints.agentpackage", - "params": "body: JSON (fields not yet documented)", - "purpose": "Bundle the adoption tool as a downloadable ZIP.", - "example": "curl -X POST http://localhost:5001/api/endpoints/adoptiontool/package -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/endpoints/agentkey", - "auth": "jwt + role:admin", - "params": "not documented", - "purpose": "Revoke the endpoint agent key: stop all managed-token endpoint traffic.", - "example": "curl -X DELETE http://localhost:5001/api/endpoints/agentkey -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/agentkey", - "auth": "jwt + role:admin", - "params": "not documented", - "purpose": "Report the endpoint agent key state (no secret).", - "example": "curl http://localhost:5001/api/endpoints/agentkey -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/endpoints/agentkey/regenerate", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Mint a fresh endpoint agent key, revoking any previous one.", - "example": "curl -X POST http://localhost:5001/api/endpoints/agentkey/regenerate -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/endpoints/alerts", - "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", - "params": "not documented", - "purpose": "Fleet alerts for one endpoint by hostname (collector-token auth).", - "example": "curl http://localhost:5001/api/endpoints/alerts -H \"X-API-Key: $KEY\"" - }, - { - "method": "POST", - "path": "/api/endpoints/alerts//acknowledge", - "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", - "params": "path: notification_id (int); body: JSON (fields not yet documented)", - "purpose": "Agent acknowledges a fleet alert (collector-token auth).", - "example": "curl -X POST http://localhost:5001/api/endpoints/alerts/1/acknowledge -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/endpoints/apppackage/", - "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", - "params": "path: appversionid (int)", - "purpose": "Agent-facing installer download (collector-token auth).", - "example": "curl http://localhost:5001/api/endpoints/apppackage/1 -H \"X-API-Key: $KEY\"" - }, - { - "method": "GET", - "path": "/api/endpoints/apppackage/app/", - "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", - "params": "path: app_id (int)", - "purpose": "Agent-facing installer download by APP (collector-token auth).", - "example": "curl http://localhost:5001/api/endpoints/apppackage/app/1 -H \"X-API-Key: $KEY\"" - }, - { - "method": "GET", - "path": "/api/endpoints/apppackage/app//", - "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", - "params": "path: app_id (int), filename (path)", - "purpose": "Agent-facing installer download by APP (collector-token auth).", - "example": "curl http://localhost:5001/api/endpoints/apppackage/app/1/example.png -H \"X-API-Key: $KEY\"" - }, - { - "method": "GET", - "path": "/api/endpoints/asset//activity", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "List panel: one chronological feed of what happened to this endpoint.", - "example": "curl http://localhost:5001/api/endpoints/asset/1/activity -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/asset//applications", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "Table panel: the installed-software (SBOM) snapshot for this endpoint.", - "example": "curl http://localhost:5001/api/endpoints/asset/1/applications -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/asset//diskhealth", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "Physical drive health rows.", - "example": "curl http://localhost:5001/api/endpoints/asset/1/diskhealth -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/asset//hardware", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "Keyvalue panel: processor, memory and system chassis inventory.", - "example": "curl http://localhost:5001/api/endpoints/asset/1/hardware -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/asset//interfaces", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "Table panel: every hardware NIC the agent last reported.", - "example": "curl http://localhost:5001/api/endpoints/asset/1/interfaces -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/asset//iphistory", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "Table panel: every address this asset has held, newest change first.", - "example": "curl http://localhost:5001/api/endpoints/asset/1/iphistory -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/asset//listeners", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "Table panel: TCP sockets in LISTEN, with the owning process.", - "example": "curl http://localhost:5001/api/endpoints/asset/1/listeners -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/asset//mappeddrives", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "List panel: mapped network drives with a health badge each.", - "example": "curl http://localhost:5001/api/endpoints/asset/1/mappeddrives -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/asset//osinfo", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "Keyvalue panel: operating system, firmware and domain membership.", - "example": "curl http://localhost:5001/api/endpoints/asset/1/osinfo -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/asset//recentevents", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "List panel: the endpoint's most recent curated events, newest first.", - "example": "curl http://localhost:5001/api/endpoints/asset/1/recentevents -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/asset//services", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "List panel: monitored services/processes with a run-state badge per row.", - "example": "curl http://localhost:5001/api/endpoints/asset/1/services -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/asset//status", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "Keyvalue panel: the endpoint's current status snapshot.", - "example": "curl http://localhost:5001/api/endpoints/asset/1/status -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/asset//storage", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "Tabs panel: volume capacity and physical drive health as two sections.", - "example": "curl http://localhost:5001/api/endpoints/asset/1/storage -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/asset//telemetrymeta", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "Lightweight freshness probe for the panels.", - "example": "curl http://localhost:5001/api/endpoints/asset/1/telemetrymeta -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/asset//usercontext", - "auth": "permission:endpoints.view", - "params": "path: asset_id (int)", - "purpose": "Keyvalue panel: session-scoped data from the interactive user probe.", - "example": "curl http://localhost:5001/api/endpoints/asset/1/usercontext -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/endpoints/collect", - "auth": "api-key (endpoints collector token)", - "params": "body: JSON (fields not yet documented)", - "purpose": "Agent telemetry ingest. Same handler as POST /api/collector/endpoints.", - "example": "curl -X POST http://localhost:5001/api/endpoints/collect -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/endpoints/commands//result", - "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", - "params": "path: command_id (int); body: JSON (fields not yet documented)", - "purpose": "Agent reports a claimed command's outcome (collector-token auth).", - "example": "curl -X POST http://localhost:5001/api/endpoints/commands/1/result -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/endpoints/commands/next", - "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", - "params": "not documented", - "purpose": "Agent long-poll: claim the next pending command for a hostname.", - "example": "curl http://localhost:5001/api/endpoints/commands/next -H \"X-API-Key: $KEY\"" - }, - { - "method": "GET", - "path": "/api/endpoints/dashboard/enrollmentpending", - "auth": "permission:endpoints.fleet", - "params": "not documented", - "purpose": "One queue row while enroll requests wait for an admin, else nothing.", - "example": "curl http://localhost:5001/api/endpoints/dashboard/enrollmentpending -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/dashboard/quiet", - "auth": "permission:endpoints.fleet", - "params": "not documented", - "purpose": "Endpoints that reported before, then went quiet past the window.", - "example": "curl http://localhost:5001/api/endpoints/dashboard/quiet -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/dashboard/reenrollblocked", - "auth": "permission:endpoints.fleet", - "params": "not documented", - "purpose": "PCs kept from re-enrolling for more than 7 days.", - "example": "curl http://localhost:5001/api/endpoints/dashboard/reenrollblocked -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/dashboard/stability", - "auth": "permission:endpoints.fleet", - "params": "not documented", - "purpose": "Endpoints with recent crash/shutdown/corruption events.", - "example": "curl http://localhost:5001/api/endpoints/dashboard/stability -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/dashboard/summary", - "auth": "permission:endpoints.fleet", - "params": "not documented", - "purpose": "High-level fleet counts for the endpoints dashboard.", - "example": "curl http://localhost:5001/api/endpoints/dashboard/summary -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/endpoints/enroll", - "auth": "none (per-IP rate limited and capped; a valid fleet key only decides auto-enroll)", - "params": "body: JSON (fields not yet documented)", - "purpose": "A PC asks for its own credential.", - "example": "curl -X POST http://localhost:5001/api/endpoints/enroll -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/endpoints/enrollment", - "auth": "permission:endpoints.fleet", - "params": "not documented", - "purpose": "Credential state of every computer, plus pending and recent requests.", - "example": "curl http://localhost:5001/api/endpoints/enrollment -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/endpoints/enrollment/assets//allow-reenroll", - "auth": "jwt + role:admin", - "params": "path: assetid (int); body: JSON (fields not yet documented)", - "purpose": "Lift a re-enrollment block. The PC enrolls on its next attempt.", - "example": "curl -X POST http://localhost:5001/api/endpoints/enrollment/assets/1/allow-reenroll -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/endpoints/enrollment/assets//revoke", - "auth": "jwt + role:admin", - "params": "path: assetid (int); body: JSON (fields not yet documented)", - "purpose": "Un-enroll a PC. Its agent gets CREDENTIAL_INVALID and enrolls again.", - "example": "curl -X POST http://localhost:5001/api/endpoints/enrollment/assets/1/revoke -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/endpoints/enrollment/migration", - "auth": "permission:endpoints.fleet", - "params": "not documented", - "purpose": "Per-computer readiness to move onto per-PC credentials, plus waves.", - "example": "curl http://localhost:5001/api/endpoints/enrollment/migration -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/endpoints/enrollment/migration/wave", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Push the agent upgrade to a batch of READY PCs.", - "example": "curl -X POST http://localhost:5001/api/endpoints/enrollment/migration/wave -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/endpoints/enrollment/requests//approve", - "auth": "jwt + role:admin", - "params": "path: requestid (int); body: JSON (fields not yet documented)", - "purpose": "Bind a pending request to the asset the admin picked.", - "example": "curl -X POST http://localhost:5001/api/endpoints/enrollment/requests/1/approve -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/endpoints/enrollment/requests//reject", - "auth": "jwt + role:admin", - "params": "path: requestid (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: endpoints.reject_enroll_request has no docstring.", - "example": "curl -X POST http://localhost:5001/api/endpoints/enrollment/requests/1/reject -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "PUT", - "path": "/api/endpoints/enrollment/settings", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Change the enrollment settings.", - "example": "curl -X PUT http://localhost:5001/api/endpoints/enrollment/settings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/endpoints/logcollections/", - "auth": "permission:endpoints.updateapp", - "params": "path: collection_id (int)", - "purpose": "One collected log including its content, for the scrollable pane.", - "example": "curl http://localhost:5001/api/endpoints/logcollections/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/logcollections//download", - "auth": "permission:endpoints.updateapp", - "params": "path: collection_id (int)", - "purpose": "Serve a collected log as a file attachment.", - "example": "curl http://localhost:5001/api/endpoints/logcollections/1/download -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/monitors", - "auth": "permission:endpoints.view", - "params": "not documented", - "purpose": "List monitor definitions (shipped and local).", - "example": "curl http://localhost:5001/api/endpoints/monitors -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/endpoints/monitors", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a user-defined (local) monitor definition.", - "example": "curl -X POST http://localhost:5001/api/endpoints/monitors -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/endpoints/monitors/", - "auth": "jwt + role:admin", - "params": "path: monitor_id (int)", - "purpose": "Delete a local monitor definition. Shipped rows are protected.", - "example": "curl -X DELETE http://localhost:5001/api/endpoints/monitors/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/endpoints/monitors/", - "auth": "jwt + role:admin", - "params": "path: monitor_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a local monitor definition. Shipped rows are read-only.", - "example": "curl -X PUT http://localhost:5001/api/endpoints/monitors/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/endpoints/monitors/resolve", - "auth": "permission:endpoints.view", - "params": "not documented", - "purpose": "Resolve the monitor set for a PC type (agent runtime fetch).", - "example": "curl http://localhost:5001/api/endpoints/monitors/resolve -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/monitors/resolved", - "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", - "params": "not documented", - "purpose": "Agent watch list: the monitors this endpoint should report on.", - "example": "curl http://localhost:5001/api/endpoints/monitors/resolved -H \"X-API-Key: $KEY\"" - }, - { - "method": "POST", - "path": "/api/endpoints/operatorreports", - "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", - "params": "body: JSON (fields not yet documented)", - "purpose": "Agent uploads one operator issue report (collector-token auth).", - "example": "curl -X POST http://localhost:5001/api/endpoints/operatorreports -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/endpoints/operatorreports//image", - "auth": "permission:endpoints.operatorreports", - "params": "path: operatorreportid (int)", - "purpose": "Serve one operator report's screenshot.", - "example": "curl http://localhost:5001/api/endpoints/operatorreports/1/image -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/panel//operatorreports", - "auth": "permission:endpoints.operatorreports", - "params": "path: asset_id (int)", - "purpose": "Operator Reports panel: this PC's recent operator issue reports.", - "example": "curl http://localhost:5001/api/endpoints/panel/1/operatorreports -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/scriptbody/", - "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", - "params": "path: script_id (int)", - "purpose": "Agent-facing script body download (collector-token auth).", - "example": "curl http://localhost:5001/api/endpoints/scriptbody/1 -H \"X-API-Key: $KEY\"" - }, - { - "method": "GET", - "path": "/api/endpoints/settings/server-cert", - "auth": "jwt + role:admin", - "params": "not documented", - "purpose": "Get details about the current server certificate.", - "example": "curl http://localhost:5001/api/endpoints/settings/server-cert -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/settings/server-cert/download", - "auth": "permission:endpoints.fleet", - "params": "not documented", - "purpose": "The PUBLIC server certificate as a DER .cer file, never the key.", - "example": "curl http://localhost:5001/api/endpoints/settings/server-cert/download -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/endpoints/settings/server-cert/generate", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Generate a new self-signed server certificate.", - "example": "curl -X POST http://localhost:5001/api/endpoints/settings/server-cert/generate -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/endpoints/settings/server-cert/live", - "auth": "permission:endpoints.fleet", - "params": "not documented", - "purpose": "The certificate PCs actually receive from this site's public address.", - "example": "curl http://localhost:5001/api/endpoints/settings/server-cert/live -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/endpoints/settings/server-cert/upload", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Upload a new server certificate and private key.", - "example": "curl -X POST http://localhost:5001/api/endpoints/settings/server-cert/upload -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/endpoints/siteagent", - "auth": "permission:endpoints.agentpackage", - "params": "not documented", - "purpose": "Report whether the site agent build has a prebuilt binary staged.", - "example": "curl http://localhost:5001/api/endpoints/siteagent -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/endpoints/siteagent/binaries/", - "auth": "permission:endpoints.agentpackage", - "params": "path: binkey (string)", - "purpose": "List staged versions for a binary (agent or tray), newest first.", - "example": "curl http://localhost:5001/api/endpoints/siteagent/binaries/x -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/endpoints/siteagent/binaries/", - "auth": "permission:endpoints.agentpackage", - "params": "path: binkey (string); body: JSON (fields not yet documented)", - "purpose": "Stage an uploaded binary as a new version.", - "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/x -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/endpoints/siteagent/binaries//adopt", - "auth": "permission:endpoints.agentpackage", - "params": "path: binkey (string); body: JSON (fields not yet documented)", - "purpose": "Adopt the unmanaged legacy flat file into the version store.", - "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/x/adopt -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "PUT", - "path": "/api/endpoints/siteagent/binaries//current", - "auth": "permission:endpoints.agentpackage", - "params": "path: binkey (string); body: JSON (fields not yet documented)", - "purpose": "Point 'current' at an already-staged version (roll back or forward).", - "example": "curl -X PUT http://localhost:5001/api/endpoints/siteagent/binaries/x/current -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/endpoints/siteagent/binaries//detect", - "auth": "permission:endpoints.agentpackage", - "params": "path: binkey (string); body: JSON (fields not yet documented)", - "purpose": "Read the PE file version of an uploaded EXE without staging it.", - "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/x/detect -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/endpoints/siteagent/bootstrap.ps1", - "auth": "api-key (single-use bootstrap token in the X-Endpoint-Bootstrap-Token header, or ?t= while that is enabled; per-IP rate limited, 404 when not usable)", - "params": "not documented", - "purpose": "The PowerShell the one-line command downloads, runs and deletes.", - "example": "curl http://localhost:5001/api/endpoints/siteagent/bootstrap.ps1" - }, - { - "method": "GET", - "path": "/api/endpoints/siteagent/bootstrap/package", - "auth": "api-key (single-use bootstrap token in the X-Endpoint-Bootstrap-Token header, or ?t= while that is enabled; per-IP rate limited, 404 when not usable)", - "params": "not documented", - "purpose": "The keyless agent package the bootstrap script installs.", - "example": "curl http://localhost:5001/api/endpoints/siteagent/bootstrap/package" - }, - { - "method": "GET", - "path": "/api/endpoints/siteagent/bootstrap/tokens", - "auth": "jwt + role:admin", - "params": "not documented", - "purpose": "Every bootstrap token, newest first. Never a secret.", - "example": "curl http://localhost:5001/api/endpoints/siteagent/bootstrap/tokens -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/endpoints/siteagent/bootstrap/tokens", - "auth": "jwt + role:admin", - "params": "body: JSON (fields not yet documented)", - "purpose": "Mint one short-lived bootstrap token. The secret is returned ONCE.", - "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/bootstrap/tokens -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/endpoints/siteagent/bootstrap/tokens//revoke", - "auth": "jwt + role:admin", - "params": "path: tokenid (int); body: JSON (fields not yet documented)", - "purpose": "Kill a bootstrap token now. Already-revoked is a no-op, not an error.", - "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/bootstrap/tokens/1/revoke -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/endpoints/siteagent/package", - "auth": "permission:endpoints.agentpackage", - "params": "body: JSON (fields not yet documented)", - "purpose": "Bundle the site agent as a downloadable ZIP.", - "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/package -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "DELETE", + "path": "/api/endpoints//lock", + "auth": "permission:endpoints.command", + "params": "path: asset_id (int)", + "purpose": "Lift the quarantine on this endpoint.", + "example": "curl -X DELETE http://localhost:5001/api/endpoints/1/lock -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/endpoints//lock", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "The endpoint lock standing on this endpoint, or null.", + "example": "curl http://localhost:5001/api/endpoints/1/lock -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "assetid": 1, + "endpointlockid": 1, + "lockedat": "2026-01-01T00:00:00.000000Z", + "lockedbyuserid": 1, + "lockedbyusername": "", + "reason": "visible to all", + "releasedat": null, + "releasedbyusername": "" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/endpoints//lock", + "auth": "permission:endpoints.command", + "params": "path: asset_id (int); body: JSON (fields not yet documented)", + "purpose": "Quarantine this endpoint against non-admin commands.", + "example": "curl -X POST http://localhost:5001/api/endpoints/1/lock -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "reason": "locked but still reconciled" + }, + "responseexample": { + "data": { + "assetid": 1, + "endpointlockid": 1, + "lockedat": "2026-01-01T00:00:00.000000Z", + "lockedbyuserid": 1, + "lockedbyusername": "", + "reason": "locked but still reconciled", + "releasedat": null, + "releasedbyusername": "" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/endpoints//logcollections", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "Collected-log history for one endpoint, newest first.", + "example": "curl http://localhost:5001/api/endpoints/1/logcollections -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints//policyholds", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "Active break-glass holds for one endpoint.", + "example": "curl http://localhost:5001/api/endpoints/1/policyholds -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ + { + "action": "service_stop", + "createdbyusername": "", + "expiresat": "2026-01-01T00:00:00.000000", + "reason": "vendor on site" } - ] - }, - { - "surface": "plugin-geenforce", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/endpoints//policyholds", + "auth": "permission:endpoints.command", + "params": "path: asset_id (int); body: JSON (fields not yet documented)", + "purpose": "Deliberately suspend desired-state reconciliation for one or more actions.", + "example": "curl -X POST http://localhost:5001/api/endpoints/1/policyholds -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "actions": [ + "service_start", + "service_stop" + ], + "minutes": 60, + "reason": "stripping the enclosure" + }, + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/geenforce/applications", - "auth": "permission:geenforce.view or geenforce.manage", - "params": "not documented", - "purpose": "The core Applications catalog (id + name) for the curated entry->app link picker in the entry editor.", - "example": "curl http://localhost:5001/api/geenforce/applications -H \"Authorization: Bearer $TOK\"" + "action": "service_stop", + "createdbyusername": "", + "expiresat": "2026-01-01T00:00:00.000000", + "reason": "stripping the enclosure" }, { - "method": "GET", - "path": "/api/geenforce/config", - "auth": "permission:geenforce.view or geenforce.manage", - "params": "not documented", - "purpose": "Plugin config: the on-share export root + the client IP allowlist.", - "example": "curl http://localhost:5001/api/geenforce/config -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/geenforce/config", - "auth": "permission:geenforce.publish", - "params": "body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: geenforce.put_config has no docstring.", - "example": "curl -X PUT http://localhost:5001/api/geenforce/config -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/geenforce/dashboard/failures", - "auth": "permission:geenforce.view or geenforce.manage", - "params": "not documented", - "purpose": "Entries that FAILED on their PC's most recent enforcement cycle.", - "example": "curl http://localhost:5001/api/geenforce/dashboard/failures -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "DELETE", - "path": "/api/geenforce/entries/", - "auth": "permission:geenforce.manage", - "params": "path: entryid (int)", - "purpose": "UNDOCUMENTED: geenforce.delete_entry has no docstring.", - "example": "curl -X DELETE http://localhost:5001/api/geenforce/entries/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/geenforce/entries/", - "auth": "permission:geenforce.manage", - "params": "path: entryid (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: geenforce.update_entry has no docstring.", - "example": "curl -X PUT http://localhost:5001/api/geenforce/entries/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/geenforce/entries//payload", - "auth": "permission:geenforce.view or geenforce.manage", - "params": "path: entryid (int)", - "purpose": "Return the stored inline payload bytes for an entry (404 if none).", - "example": "curl http://localhost:5001/api/geenforce/entries/1/payload -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/geenforce/entries//payload", - "auth": "permission:geenforce.publish", - "params": "path: entryid (int); body: JSON (fields not yet documented)", - "purpose": "Store an inline payload (<= 1 MB) for an entry and point the entry at it.", - "example": "curl -X POST http://localhost:5001/api/geenforce/entries/1/payload -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/geenforce/manifest", - "auth": "api-key (geenforce fetch token, or the IP allowlist)", - "params": "not documented", - "purpose": "Serve the current published manifest for a scope (fat-client: full scope).", - "example": "curl http://localhost:5001/api/geenforce/manifest -H \"X-API-Key: $KEY\"" - }, - { - "method": "GET", - "path": "/api/geenforce/payload/", - "auth": "api-key (geenforce fetch token, or the IP allowlist)", - "params": "path: sha256 (string)", - "purpose": "Serve a payload blob by content hash over HTTPS.", - "example": "curl http://localhost:5001/api/geenforce/payload/x -H \"X-API-Key: $KEY\"" - }, - { - "method": "POST", - "path": "/api/geenforce/report", - "auth": "api-key (geenforce report token)", - "params": "body: JSON (fields not yet documented)", - "purpose": "Record one PC's enforcement cycle: applied version (did it receive the update?) + per-entry self-heal outcomes (installed/skipped/failed).", - "example": "curl -X POST http://localhost:5001/api/geenforce/report -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/geenforce/reports", - "auth": "permission:geenforce.view or geenforce.manage", - "params": "not documented", - "purpose": "Latest enforcement report per PC (fleet compliance view).", - "example": "curl http://localhost:5001/api/geenforce/reports -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/geenforce/reports/", - "auth": "permission:geenforce.view or geenforce.manage", - "params": "path: reportid (int)", - "purpose": "One report with per-entry outcomes (self-heal / failure detail).", - "example": "curl http://localhost:5001/api/geenforce/reports/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/geenforce/scopes", - "auth": "permission:geenforce.view or geenforce.manage", - "params": "not documented", - "purpose": "List imaging PC-type scopes with entry + published-version counts.", - "example": "curl http://localhost:5001/api/geenforce/scopes -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/geenforce/scopes", - "auth": "permission:geenforce.manage", - "params": "body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: geenforce.create_scope has no docstring.", - "example": "curl -X POST http://localhost:5001/api/geenforce/scopes -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/geenforce/scopes/", - "auth": "permission:geenforce.manage", - "params": "path: scopeid (int)", - "purpose": "UNDOCUMENTED: geenforce.delete_scope has no docstring.", - "example": "curl -X DELETE http://localhost:5001/api/geenforce/scopes/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/geenforce/scopes/", - "auth": "permission:geenforce.view or geenforce.manage", - "params": "path: scopeid (int)", - "purpose": "UNDOCUMENTED: geenforce.get_scope has no docstring.", - "example": "curl http://localhost:5001/api/geenforce/scopes/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/geenforce/scopes/", - "auth": "permission:geenforce.manage", - "params": "path: scopeid (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: geenforce.update_scope has no docstring.", - "example": "curl -X PUT http://localhost:5001/api/geenforce/scopes/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/geenforce/scopes//compliance", - "auth": "permission:geenforce.view or geenforce.manage", - "params": "path: scopeid (int)", - "purpose": "Fleet-install coverage per app-linked entry (from collected PC data).", - "example": "curl http://localhost:5001/api/geenforce/scopes/1/compliance -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/geenforce/scopes//entries", - "auth": "permission:geenforce.manage", - "params": "path: scopeid (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: geenforce.create_entry has no docstring.", - "example": "curl -X POST http://localhost:5001/api/geenforce/scopes/1/entries -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "PUT", - "path": "/api/geenforce/scopes//entries/reorder", - "auth": "permission:geenforce.manage", - "params": "path: scopeid (int); body: JSON (fields not yet documented)", - "purpose": "Set entry order from a list of entryids (the ordering contract).", - "example": "curl -X PUT http://localhost:5001/api/geenforce/scopes/1/entries/reorder -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/geenforce/scopes//export-share", - "auth": "permission:geenforce.publish", - "params": "path: scopeid (int); body: JSON (fields not yet documented)", - "purpose": "Write the scope's current published JSON to the configured share root (backing up the old file to _meta/history first).", - "example": "curl -X POST http://localhost:5001/api/geenforce/scopes/1/export-share -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/geenforce/scopes//preview", - "auth": "permission:geenforce.view or geenforce.manage", - "params": "path: scopeid (int)", - "purpose": "Render the DRAFT manifest JSON a publish would freeze (review before ship).", - "example": "curl http://localhost:5001/api/geenforce/scopes/1/preview -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/geenforce/scopes//publish", - "auth": "permission:geenforce.publish", - "params": "path: scopeid (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: geenforce.publish_scope_route has no docstring.", - "example": "curl -X POST http://localhost:5001/api/geenforce/scopes/1/publish -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/geenforce/scopes//publish-preflight", - "auth": "permission:geenforce.view or geenforce.manage", - "params": "path: scopeid (int)", - "purpose": "Would publishing this scope outrun the fleet's enforcer lib?", - "example": "curl http://localhost:5001/api/geenforce/scopes/1/publish-preflight -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/geenforce/scopes//rollback", - "auth": "permission:geenforce.publish", - "params": "path: scopeid (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: geenforce.rollback_scope_route has no docstring.", - "example": "curl -X POST http://localhost:5001/api/geenforce/scopes/1/rollback -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/geenforce/scopes//simulate", - "auth": "permission:geenforce.view or geenforce.manage", - "params": "path: scopeid (int)", - "purpose": "Which draft entries apply to a given machine profile, and why the rest are filtered out (reuses the engine-mirror filters).", - "example": "curl http://localhost:5001/api/geenforce/scopes/1/simulate -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/geenforce/scopes//versions", - "auth": "permission:geenforce.view or geenforce.manage", - "params": "path: scopeid (int)", - "purpose": "UNDOCUMENTED: geenforce.list_versions has no docstring.", - "example": "curl http://localhost:5001/api/geenforce/scopes/1/versions -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/geenforce/scopes//versions/", - "auth": "permission:geenforce.view or geenforce.manage", - "params": "path: scopeid (int), versionnumber (int)", - "purpose": "UNDOCUMENTED: geenforce.get_version has no docstring.", - "example": "curl http://localhost:5001/api/geenforce/scopes/1/versions/1 -H \"Authorization: Bearer $TOK\"" + "action": "service_start", + "createdbyusername": "", + "expiresat": "2026-01-01T00:00:00.000000", + "reason": "stripping the enclosure" } - ] - }, - { - "surface": "plugin-guides", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/endpoints//policyholds/", + "auth": "permission:endpoints.command", + "params": "path: asset_id (int), action (string)", + "purpose": "End a hold early, resuming reconciliation of one action.", + "example": "curl -X DELETE http://localhost:5001/api/endpoints/1/policyholds/x -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/endpoints//scriptruns", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "The last few run_script commands for one endpoint, newest first.", + "example": "curl http://localhost:5001/api/endpoints/1/scriptruns -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "limit", + "type": "integer", + "required": false, + "default": 5 + } + ], + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/guides", - "auth": "permission:guides.view", - "params": "not documented", - "purpose": "Published guides, plus the caller's own drafts if they can author.", - "example": "curl http://localhost:5001/api/guides -H \"Authorization: Bearer $TOK\"" + "action": "run_script", + "assetid": 1, + "claimedat": null, + "completedat": null, + "createdat": "2026-01-01T00:00:00Z", + "endpointcommandid": 6, + "requestedby": "", + "resultdetail": null, + "resultmessage": null, + "scriptname": "test-open-notepad", + "status": "succeeded", + "target": "script:1", + "targetkind": null }, { - "method": "POST", - "path": "/api/guides", - "auth": "permission:guides.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: guides.create_guide has no docstring.", - "example": "curl -X POST http://localhost:5001/api/guides -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "action": "run_script", + "assetid": 1, + "claimedat": null, + "completedat": null, + "createdat": "2026-01-01T00:00:00Z", + "endpointcommandid": 5, + "requestedby": "", + "resultdetail": null, + "resultmessage": null, + "scriptname": "test-open-notepad", + "status": "succeeded", + "target": "script:1", + "targetkind": null }, { - "method": "DELETE", - "path": "/api/guides/", - "auth": "permission:guides.delete", - "params": "path: guideid (int)", - "purpose": "UNDOCUMENTED: guides.delete_guide has no docstring.", - "example": "curl -X DELETE http://localhost:5001/api/guides/1 -H \"Authorization: Bearer $TOK\"" + "action": "run_script", + "assetid": 1, + "claimedat": null, + "completedat": null, + "createdat": "2026-01-01T00:00:00Z", + "endpointcommandid": 4, + "requestedby": "", + "resultdetail": null, + "resultmessage": null, + "scriptname": "test-open-notepad", + "status": "succeeded", + "target": "script:1", + "targetkind": null }, - { - "method": "PUT", - "path": "/api/guides/", - "auth": "permission:guides.view", - "params": "path: guideid (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: guides.update_guide has no docstring.", - "example": "curl -X PUT http://localhost:5001/api/guides/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/endpoints/adoptiontool/package", + "auth": "permission:endpoints.agentpackage", + "params": "body: JSON (fields not yet documented)", + "purpose": "Bundle the adoption tool as a downloadable ZIP.", + "example": "curl -X POST http://localhost:5001/api/endpoints/adoptiontool/package -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/endpoints/agentkey", + "auth": "jwt + role:admin", + "params": "not documented", + "purpose": "Revoke the endpoint agent key: stop all managed-token endpoint traffic.", + "example": "curl -X DELETE http://localhost:5001/api/endpoints/agentkey -H \"Authorization: Bearer $TOK\"", + "requestexample": {}, + "responseexample": { + "data": { + "envkeyfallback": true, + "key": null, + "provisioned": false, + "revoked": 0 + }, + "message": "Endpoint agent key revoked", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/endpoints/agentkey", + "auth": "jwt + role:admin", + "params": "not documented", + "purpose": "Report the endpoint agent key state (no secret).", + "example": "curl http://localhost:5001/api/endpoints/agentkey -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/endpoints/agentkey/regenerate", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Mint a fresh endpoint agent key, revoking any previous one.", + "example": "curl -X POST http://localhost:5001/api/endpoints/agentkey/regenerate -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": {}, + "responseexample": { + "data": { + "envkeyfallback": false, + "key": { + "createddate": "2026-01-01T00:00:00.000000Z", + "displayprefix": "", + "expiresat": null, + "isactive": true, + "isexpired": false, + "lastusedat": null, + "name": "Endpoint agent key", + "resourcescopes": null, + "scopes": [ + "collector.ingest.endpoints" + ], + "tokenid": 1, + "tokenprefix": "", + "userid": 1 }, + "provisioned": true, + "secret": "", + "warning": "Save this key now. It will not be shown again. Configure it as the agent X-API-Key." + }, + "message": "Endpoint agent key generated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/endpoints/alerts", + "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", + "params": "not documented", + "purpose": "Fleet alerts for one endpoint by hostname (collector-token auth).", + "example": "curl http://localhost:5001/api/endpoints/alerts -H \"X-API-Key: $KEY\"", + "queryparams": [ + { + "name": "hostname", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [ { - "method": "POST", - "path": "/api/guides//publish", - "auth": "permission:guides.publish", - "params": "path: guideid (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: guides.publish_guide has no docstring.", - "example": "curl -X POST http://localhost:5001/api/guides/1/publish -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/guides//restore/", - "auth": "permission:guides.view", - "params": "path: guideid (int), revisionid (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: guides.restore_revision has no docstring.", - "example": "curl -X POST http://localhost:5001/api/guides/1/restore/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/guides//revisions", - "auth": "permission:guides.view", - "params": "path: guideid (int)", - "purpose": "UNDOCUMENTED: guides.list_revisions has no docstring.", - "example": "curl http://localhost:5001/api/guides/1/revisions -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/guides//unpublish", - "auth": "permission:guides.publish", - "params": "path: guideid (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: guides.unpublish_guide has no docstring.", - "example": "curl -X POST http://localhost:5001/api/guides/1/unpublish -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/guides//working", - "auth": "permission:guides.view", - "params": "path: guideid (int)", - "purpose": "The working copy, for the editor.", - "example": "curl http://localhost:5001/api/guides/1/working -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/guides/", - "auth": "permission:guides.view", - "params": "path: slug (string)", - "purpose": "What a reader gets: the published revision.", - "example": "curl http://localhost:5001/api/guides/x -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/guides/images", - "auth": "permission:guides.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Take a pasted or dropped image.", - "example": "curl -X POST http://localhost:5001/api/guides/images -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/guides/images/", - "auth": "permission:guides.view", - "params": "path: imageid (int)", - "purpose": "The image itself, under the same rule as the guide that holds it.", - "example": "curl http://localhost:5001/api/guides/images/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/guides/revisions/", - "auth": "permission:guides.view", - "params": "path: revisionid (int)", - "purpose": "One revision by id.", - "example": "curl http://localhost:5001/api/guides/revisions/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/guides/tags", - "auth": "permission:guides.view", - "params": "not documented", - "purpose": "Every tag in use, with a count, for the filter chips.", - "example": "curl http://localhost:5001/api/guides/tags -H \"Authorization: Bearer $TOK\"" + "message": "Scheduled maintenance at 3:30 PM", + "notificationId": 1, + "notificationid": 1, + "timeout": 0, + "title": "Scheduled maintenance at 3:30 PM", + "type": "Fleet Alert", + "typeColor": "#ffc107", + "typecolor": "#ffc107" } - ] - }, - { - "surface": "plugin-knowledgebase", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/endpoints/alerts//acknowledge", + "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", + "params": "path: notification_id (int); body: JSON (fields not yet documented)", + "purpose": "Agent acknowledges a fleet alert (collector-token auth).", + "example": "curl -X POST http://localhost:5001/api/endpoints/alerts/1/acknowledge -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "assetid": 1, + "hostname": "" + }, + "responseexample": { + "data": { + "acknowledged": true + }, + "message": "Alert already acknowledged", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/endpoints/apppackage/", + "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", + "params": "path: appversionid (int)", + "purpose": "Agent-facing installer download (collector-token auth).", + "example": "curl http://localhost:5001/api/endpoints/apppackage/1 -H \"X-API-Key: $KEY\"" + }, + { + "method": "GET", + "path": "/api/endpoints/apppackage/app/", + "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", + "params": "path: app_id (int)", + "purpose": "Agent-facing installer download by APP (collector-token auth).", + "example": "curl http://localhost:5001/api/endpoints/apppackage/app/1 -H \"X-API-Key: $KEY\"" + }, + { + "method": "GET", + "path": "/api/endpoints/apppackage/app//", + "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", + "params": "path: app_id (int), filename (path)", + "purpose": "Agent-facing installer download by APP (collector-token auth).", + "example": "curl http://localhost:5001/api/endpoints/apppackage/app/1/example.png -H \"X-API-Key: $KEY\"" + }, + { + "method": "GET", + "path": "/api/endpoints/asset//activity", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "List panel: one chronological feed of what happened to this endpoint.", + "example": "curl http://localhost:5001/api/endpoints/asset/1/activity -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/asset//applications", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "Table panel: the installed-software (SBOM) snapshot for this endpoint.", + "example": "curl http://localhost:5001/api/endpoints/asset/1/applications -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/asset//diskhealth", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "Physical drive health rows.", + "example": "curl http://localhost:5001/api/endpoints/asset/1/diskhealth -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/asset//hardware", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "Keyvalue panel: processor, memory and system chassis inventory.", + "example": "curl http://localhost:5001/api/endpoints/asset/1/hardware -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/asset//interfaces", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "Table panel: every hardware NIC the agent last reported.", + "example": "curl http://localhost:5001/api/endpoints/asset/1/interfaces -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/asset//iphistory", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "Table panel: every address this asset has held, newest change first.", + "example": "curl http://localhost:5001/api/endpoints/asset/1/iphistory -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/asset//listeners", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "Table panel: TCP sockets in LISTEN, with the owning process.", + "example": "curl http://localhost:5001/api/endpoints/asset/1/listeners -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/asset//mappeddrives", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "List panel: mapped network drives with a health badge each.", + "example": "curl http://localhost:5001/api/endpoints/asset/1/mappeddrives -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/asset//osinfo", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "Keyvalue panel: operating system, firmware and domain membership.", + "example": "curl http://localhost:5001/api/endpoints/asset/1/osinfo -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/asset//recentevents", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "List panel: the endpoint's most recent curated events, newest first.", + "example": "curl http://localhost:5001/api/endpoints/asset/1/recentevents -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/asset//services", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "List panel: monitored services/processes with a run-state badge per row.", + "example": "curl http://localhost:5001/api/endpoints/asset/1/services -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/knowledgebase", - "auth": "permission:kb.view", - "params": "not documented", - "purpose": "List all knowledge base articles.", - "example": "curl http://localhost:5001/api/knowledgebase -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/knowledgebase", - "auth": "permission:kb.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new knowledge base article.", - "example": "curl -X POST http://localhost:5001/api/knowledgebase -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/knowledgebase/", - "auth": "permission:kb.delete", - "params": "path: link_id (int)", - "purpose": "Delete (deactivate) a knowledge base article.", - "example": "curl -X DELETE http://localhost:5001/api/knowledgebase/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/knowledgebase/", - "auth": "permission:kb.view", - "params": "path: link_id (int)", - "purpose": "Get a single knowledge base article.", - "example": "curl http://localhost:5001/api/knowledgebase/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/knowledgebase/", - "auth": "permission:kb.edit", - "params": "path: link_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a knowledge base article.", - "example": "curl -X PUT http://localhost:5001/api/knowledgebase/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/knowledgebase//click", - "auth": "permission:kb.view", - "params": "path: link_id (int); body: JSON (fields not yet documented)", - "purpose": "Increment the click counter for an article the caller is allowed to read.", - "example": "curl -X POST http://localhost:5001/api/knowledgebase/1/click -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/knowledgebase/stats", - "auth": "permission:kb.view", - "params": "not documented", - "purpose": "Get knowledge base statistics.", - "example": "curl http://localhost:5001/api/knowledgebase/stats -H \"Authorization: Bearer $TOK\"" + "action": { + "running": false, + "start": "process_start", + "status": "process_status", + "stop": "process_stop", + "target": "dncMain.exe", + "targetkind": "process" + }, + "drift": true, + "kind": "process", + "name": "eDNC", + "statecolor": "gray", + "statelabel": "Unknown" } - ] - }, - { - "surface": "plugin-machines", - "endpoints": [ - { - "method": "GET", - "path": "/api/machines", - "auth": "permission:machines.view", - "params": "not documented", - "purpose": "List all machines with filtering and pagination.", - "example": "curl http://localhost:5001/api/machines -H \"Authorization: Bearer $TOK\"" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/endpoints/asset//status", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "Keyvalue panel: the endpoint's current status snapshot.", + "example": "curl http://localhost:5001/api/endpoints/asset/1/status -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "agentbehind": false, + "agentcurrent": "1.6.3", + "agentversion": "1.20.0", + "credential": { + "credentialprefix": "", + "enrolledvia": "auto", + "issuedat": "2026-01-01T00:00:00.000000Z", + "lastusedat": "2026-01-01T00:00:00.000000Z", + "reenrollblocked": true, + "reenrollblockedreason": "rollback", + "revokedat": "2026-01-01T00:00:00.000000Z", + "state": "reenrollblocked", + "statelabel": "Re-enroll blocked" }, + "fields": [ + { + "label": "Agent", + "mono": true, + "value": "1.20.0" + }, + { + "format": "datetime", + "label": "Last reported", + "mono": false, + "value": "2026-01-01T00:00:00.000000Z" + }, + { + "label": "Credential", + "mono": false, + "value": "Re-enroll blocked" + }, + "" + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/endpoints/asset//storage", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "Tabs panel: volume capacity and physical drive health as two sections.", + "example": "curl http://localhost:5001/api/endpoints/asset/1/storage -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/asset//telemetrymeta", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "Lightweight freshness probe for the panels.", + "example": "curl http://localhost:5001/api/endpoints/asset/1/telemetrymeta -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/asset//usercontext", + "auth": "permission:endpoints.view", + "params": "path: asset_id (int)", + "purpose": "Keyvalue panel: session-scoped data from the interactive user probe.", + "example": "curl http://localhost:5001/api/endpoints/asset/1/usercontext -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/endpoints/collect", + "auth": "api-key (endpoints collector token)", + "params": "body: JSON (fields not yet documented)", + "purpose": "Agent telemetry ingest. Same handler as POST /api/collector/endpoints.", + "example": "curl -X POST http://localhost:5001/api/endpoints/collect -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "agentversion": "1.20.0", + "hostname": "" + }, + "responseexample": { + "data": { + "action": "created", + "assetid": 1, + "identityvalue": "BAY-01", + "status": "ok", + "warnings": [] + }, + "message": "endpoints collector created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/endpoints/commands//result", + "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", + "params": "path: command_id (int); body: JSON (fields not yet documented)", + "purpose": "Agent reports a claimed command's outcome (collector-token auth).", + "example": "curl -X POST http://localhost:5001/api/endpoints/commands/1/result -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'", + "queryparams": [ + { + "name": "hostname", + "type": "string", + "required": false + } + ], + "requestexample": { + "succeeded": true + }, + "responseexample": { + "data": { + "action": "mtconnect_status", + "assetid": 1, + "claimedat": null, + "completedat": "2026-01-01T00:00:00.000000Z", + "createdat": "2026-01-01T00:00:00.000000Z", + "endpointcommandid": 1, + "requestedby": "", + "resultmessage": null, + "status": "succeeded", + "target": null, + "targetkind": null + }, + "message": "Result recorded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/endpoints/commands/next", + "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", + "params": "not documented", + "purpose": "Agent long-poll: claim the next pending command for a hostname.", + "example": "curl http://localhost:5001/api/endpoints/commands/next -H \"X-API-Key: $KEY\"", + "queryparams": [ + { + "name": "hostname", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": { + "action": "mtconnect_status", + "endpointcommandid": 1, + "target": null, + "targetkind": null + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/endpoints/dashboard/enrollmentpending", + "auth": "permission:endpoints.fleet", + "params": "not documented", + "purpose": "One queue row while enroll requests wait for an admin, else nothing.", + "example": "curl http://localhost:5001/api/endpoints/dashboard/enrollmentpending -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "POST", - "path": "/api/machines", - "auth": "permission:machines.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create new machine (creates both Asset and Machine records).", - "example": "curl -X POST http://localhost:5001/api/machines -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/machines/", - "auth": "permission:machines.delete", - "params": "path: machine_id (int)", - "purpose": "Delete (soft delete) machine.", - "example": "curl -X DELETE http://localhost:5001/api/machines/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/machines/", - "auth": "permission:machines.view", - "params": "path: machine_id (int)", - "purpose": "Get a single machine item with full details.", - "example": "curl http://localhost:5001/api/machines/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/machines/", - "auth": "permission:machines.edit", - "params": "path: machine_id (int); body: JSON (fields not yet documented)", - "purpose": "Update machine (both Asset and Machine records).", - "example": "curl -X PUT http://localhost:5001/api/machines/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/machines/by-asset/", - "auth": "permission:machines.view", - "params": "path: asset_id (int)", - "purpose": "Get machine data by asset ID.", - "example": "curl http://localhost:5001/api/machines/by-asset/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/machines/dashboard/outofservice", - "auth": "permission:machines.view", - "params": "not documented", - "purpose": "Machines whose status says they are not running.", - "example": "curl http://localhost:5001/api/machines/dashboard/outofservice -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/machines/dashboard/summary", - "auth": "permission:machines.view", - "params": "not documented", - "purpose": "Get machine dashboard summary data.", - "example": "curl http://localhost:5001/api/machines/dashboard/summary -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/machines/types", - "auth": "permission:machines.view", - "params": "not documented", - "purpose": "List all machine types.", - "example": "curl http://localhost:5001/api/machines/types -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/machines/types", - "auth": "permission:machines.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new machine type.", - "example": "curl -X POST http://localhost:5001/api/machines/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/machines/types/", - "auth": "permission:machines.delete", - "params": "path: type_id (int)", - "purpose": "Delete a machine type. Refused if any asset still uses it.", - "example": "curl -X DELETE http://localhost:5001/api/machines/types/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/machines/types/", - "auth": "permission:machines.view", - "params": "path: type_id (int)", - "purpose": "Get a single machine type.", - "example": "curl http://localhost:5001/api/machines/types/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/machines/types/", - "auth": "permission:machines.edit", - "params": "path: type_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a machine type.", - "example": "curl -X PUT http://localhost:5001/api/machines/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "pendingcount": 2, + "summary": "2 PCs waiting for enrollment approval" } - ] - }, - { - "surface": "plugin-measuringtools", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/endpoints/dashboard/quiet", + "auth": "permission:endpoints.fleet", + "params": "not documented", + "purpose": "Endpoints that reported before, then went quiet past the window.", + "example": "curl http://localhost:5001/api/endpoints/dashboard/quiet -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/dashboard/reenrollblocked", + "auth": "permission:endpoints.fleet", + "params": "not documented", + "purpose": "PCs kept from re-enrolling for more than 7 days.", + "example": "curl http://localhost:5001/api/endpoints/dashboard/reenrollblocked -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/measuringtools", - "auth": "permission:measuringtools.view", - "params": "not documented", - "purpose": "List measuring tools with filters + pagination.", - "example": "curl http://localhost:5001/api/measuringtools -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/measuringtools", - "auth": "permission:measuringtools.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a measuring tool: one Asset core row + one extension row.", - "example": "curl -X POST http://localhost:5001/api/measuringtools -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/measuringtools/", - "auth": "permission:measuringtools.delete", - "params": "path: tool_id (int)", - "purpose": "Soft delete a measuring tool (deactivates its asset).", - "example": "curl -X DELETE http://localhost:5001/api/measuringtools/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/measuringtools/", - "auth": "permission:measuringtools.view", - "params": "path: tool_id (int)", - "purpose": "Get one measuring tool (asset core merged with the extension).", - "example": "curl http://localhost:5001/api/measuringtools/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/measuringtools/", - "auth": "permission:measuringtools.edit", - "params": "path: tool_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a measuring tool (asset core fields + extension in one payload).", - "example": "curl -X PUT http://localhost:5001/api/measuringtools/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/measuringtools/by-asset/", - "auth": "permission:measuringtools.view", - "params": "path: asset_id (int)", - "purpose": "Get a measuring tool by its core asset id.", - "example": "curl http://localhost:5001/api/measuringtools/by-asset/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/measuringtools/map-overlay", - "auth": "permission:measuringtools.view", - "params": "not documented", - "purpose": "Calibration-status overlay for active measuring tools.", - "example": "curl http://localhost:5001/api/measuringtools/map-overlay -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/measuringtools/report/calibration", - "auth": "permission:measuringtools.view", - "params": "not documented", - "purpose": "Counts + lists bucketed by derived calibration status.", - "example": "curl http://localhost:5001/api/measuringtools/report/calibration -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/measuringtools/types", - "auth": "permission:measuringtools.view", - "params": "not documented", - "purpose": "List measuring-tool types.", - "example": "curl http://localhost:5001/api/measuringtools/types -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/measuringtools/types", - "auth": "permission:measuringtools.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a measuring-tool type (reactivates a soft-deleted same-named one).", - "example": "curl -X POST http://localhost:5001/api/measuringtools/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/measuringtools/types/", - "auth": "permission:measuringtools.delete", - "params": "path: type_id (int)", - "purpose": "Delete a measuring-tool type. Refused if any tool still uses it.", - "example": "curl -X DELETE http://localhost:5001/api/measuringtools/types/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/measuringtools/types/", - "auth": "permission:measuringtools.view", - "params": "path: type_id (int)", - "purpose": "Get one measuring-tool type.", - "example": "curl http://localhost:5001/api/measuringtools/types/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/measuringtools/types/", - "auth": "permission:measuringtools.edit", - "params": "path: type_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a measuring-tool type.", - "example": "curl -X PUT http://localhost:5001/api/measuringtools/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "assetid": 2, + "blockeddays": 9, + "computerid": 2, + "hostname": "", + "reason": "rollback" } - ] - }, - { - "surface": "plugin-network", - "endpoints": [ - { - "method": "GET", - "path": "/api/network", - "auth": "permission:network.view", - "params": "not documented", - "purpose": "List all network devices with filtering and pagination.", - "example": "curl http://localhost:5001/api/network -H \"Authorization: Bearer $TOK\"" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/endpoints/dashboard/stability", + "auth": "permission:endpoints.fleet", + "params": "not documented", + "purpose": "Endpoints with recent crash/shutdown/corruption events.", + "example": "curl http://localhost:5001/api/endpoints/dashboard/stability -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/dashboard/summary", + "auth": "permission:endpoints.fleet", + "params": "not documented", + "purpose": "High-level fleet counts for the endpoints dashboard.", + "example": "curl http://localhost:5001/api/endpoints/dashboard/summary -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/endpoints/enroll", + "auth": "none (per-IP rate limited and capped; a valid fleet key only decides auto-enroll)", + "params": "body: JSON (fields not yet documented)", + "purpose": "A PC asks for its own credential.", + "example": "curl -X POST http://localhost:5001/api/endpoints/enroll -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "agentversion": "1.20.0", + "deviceid": null, + "enrollnonce": "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm", + "hostname": "", + "serialnumber": "" + }, + "responseexample": { + "data": { + "assetid": 1, + "credential": "", + "status": "enrolled" + }, + "message": "Endpoint enrolled", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/endpoints/enrollment", + "auth": "permission:endpoints.fleet", + "params": "not documented", + "purpose": "Credential state of every computer, plus pending and recent requests.", + "example": "curl http://localhost:5001/api/endpoints/enrollment -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "state", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": { + "computers": [ + { + "assetid": 1, + "credentialprefix": "", + "enrolledvia": "auto", + "hostname": "", + "issuedat": "2026-01-01T00:00:00.000000Z", + "lastusedat": null, + "reenrollblocked": false, + "reenrollblockedreason": null, + "serialnumber": "", + "state": "enrolled" + }, + { + "assetid": 2, + "credentialprefix": "", + "enrolledvia": null, + "hostname": "", + "issuedat": null, + "lastusedat": null, + "reenrollblocked": false, + "reenrollblockedreason": null, + "serialnumber": "", + "state": "notenrolled" + }, + { + "assetid": 3, + "credentialprefix": "", + "enrolledvia": null, + "hostname": "", + "issuedat": null, + "lastusedat": null, + "reenrollblocked": false, + "reenrollblockedreason": null, + "serialnumber": "", + "state": "pending" + } + ], + "counts": { + "enrolled": 1, + "notenrolled": 1, + "pending": 1, + "pendingcomputers": 1 }, - { - "method": "POST", - "path": "/api/network", - "auth": "permission:network.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create new network device (creates both Asset and NetworkDevice records).", - "example": "curl -X POST http://localhost:5001/api/network -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/network/", - "auth": "permission:network.delete", - "params": "path: device_id (int)", - "purpose": "Delete (soft delete) network device.", - "example": "curl -X DELETE http://localhost:5001/api/network/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/network/", - "auth": "permission:network.view", - "params": "path: device_id (int)", - "purpose": "Get a single network device with full details.", - "example": "curl http://localhost:5001/api/network/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/network/", - "auth": "permission:network.edit", - "params": "path: device_id (int); body: JSON (fields not yet documented)", - "purpose": "Update network device (both Asset and NetworkDevice records).", - "example": "curl -X PUT http://localhost:5001/api/network/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/network/by-asset/", - "auth": "permission:network.view", - "params": "path: asset_id (int)", - "purpose": "Get network device data by asset ID.", - "example": "curl http://localhost:5001/api/network/by-asset/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/network/by-hostname/", - "auth": "permission:network.view", - "params": "path: hostname (string)", - "purpose": "Get network device by hostname.", - "example": "curl http://localhost:5001/api/network/by-hostname/x -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/network/dashboard/summary", - "auth": "permission:network.view", - "params": "not documented", - "purpose": "Get network device dashboard summary data.", - "example": "curl http://localhost:5001/api/network/dashboard/summary -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/network/subnets", - "auth": "permission:network.view", - "params": "not documented", - "purpose": "List all subnets with filtering and pagination.", - "example": "curl http://localhost:5001/api/network/subnets -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/network/subnets", - "auth": "permission:network.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new subnet.", - "example": "curl -X POST http://localhost:5001/api/network/subnets -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/network/subnets/", - "auth": "permission:network.delete", - "params": "path: subnet_id (int)", - "purpose": "Delete (soft delete) a subnet.", - "example": "curl -X DELETE http://localhost:5001/api/network/subnets/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/network/subnets/", - "auth": "permission:network.view", - "params": "path: subnet_id (int)", - "purpose": "Get a single subnet plus the network devices whose primary IP falls in its CIDR range.", - "example": "curl http://localhost:5001/api/network/subnets/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/network/subnets/", - "auth": "permission:network.edit", - "params": "path: subnet_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a subnet.", - "example": "curl -X PUT http://localhost:5001/api/network/subnets/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/network/types", - "auth": "permission:network.view", - "params": "not documented", - "purpose": "List all network device types.", - "example": "curl http://localhost:5001/api/network/types -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/network/types", - "auth": "permission:network.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new network device type.", - "example": "curl -X POST http://localhost:5001/api/network/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/network/types/", - "auth": "permission:network.delete", - "params": "path: type_id (int)", - "purpose": "Delete a network device type. Refused if any device still uses it.", - "example": "curl -X DELETE http://localhost:5001/api/network/types/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/network/types/", - "auth": "permission:network.view", - "params": "path: type_id (int)", - "purpose": "Get a single network device type.", - "example": "curl http://localhost:5001/api/network/types/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/network/types/", - "auth": "permission:network.edit", - "params": "path: type_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a network device type.", - "example": "curl -X PUT http://localhost:5001/api/network/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/network/vlans", - "auth": "permission:network.view", - "params": "not documented", - "purpose": "List all VLANs with filtering and pagination.", - "example": "curl http://localhost:5001/api/network/vlans -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/network/vlans", - "auth": "permission:network.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new VLAN.", - "example": "curl -X POST http://localhost:5001/api/network/vlans -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/network/vlans/", - "auth": "permission:network.delete", - "params": "path: vlan_id (int)", - "purpose": "Delete (soft delete) a VLAN.", - "example": "curl -X DELETE http://localhost:5001/api/network/vlans/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/network/vlans/", - "auth": "permission:network.view", - "params": "path: vlan_id (int)", - "purpose": "Get a single VLAN with its subnets.", - "example": "curl http://localhost:5001/api/network/vlans/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/network/vlans/", - "auth": "permission:network.edit", - "params": "path: vlan_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a VLAN.", - "example": "curl -X PUT http://localhost:5001/api/network/vlans/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "requests": [ + { + "agentversion": "1.20.0", + "decidedat": null, + "decidedby": null, + "deviceid": null, + "hostname": "", + "lastseenat": "2026-01-01T00:00:00.000000Z", + "matchedassetid": 3, + "reason": "no serial on record", + "requestedat": "2026-01-01T00:00:00.000000Z", + "requestid": "00000000", + "serialnumber": "", + "sourceip": "127.0.0.1", + "status": "pending" + }, + { + "agentversion": "1.20.0", + "decidedat": null, + "decidedby": null, + "deviceid": null, + "hostname": "", + "lastseenat": "2026-01-01T00:00:00.000000Z", + "matchedassetid": 1, + "reason": null, + "requestedat": "2026-01-01T00:00:00.000000Z", + "requestid": "00000000", + "serialnumber": "", + "sourceip": "127.0.0.1", + "status": "issued" + } + ], + "settings": { + "autoenroll": true, + "maxpendingenrollments": 500, + "revokeddeniessharedkey": false, + "sharedkeyallowed": true } + }, + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 3, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/endpoints/enrollment/assets//allow-reenroll", + "auth": "jwt + role:admin", + "params": "path: assetid (int); body: JSON (fields not yet documented)", + "purpose": "Lift a re-enrollment block. The PC enrolls on its next attempt.", + "example": "curl -X POST http://localhost:5001/api/endpoints/enrollment/assets/1/allow-reenroll -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "assetid": 1, + "credentialid": 1, + "credentialprefix": "", + "enrolledvia": "auto", + "issuedat": "2026-01-01T00:00:00.000000Z", + "lastusedat": null, + "reenrollblocked": false, + "reenrollblockedreason": null, + "revokedat": "2026-01-01T00:00:00.000000Z", + "revokedby": "testadmin" + }, + "message": "Re-enrollment allowed", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/endpoints/enrollment/assets//revoke", + "auth": "jwt + role:admin", + "params": "path: assetid (int); body: JSON (fields not yet documented)", + "purpose": "Un-enroll a PC. Its agent gets CREDENTIAL_INVALID and enrolls again.", + "example": "curl -X POST http://localhost:5001/api/endpoints/enrollment/assets/1/revoke -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "blockreenroll": true, + "reason": "rollback" + }, + "responseexample": { + "data": { + "assetid": 1, + "credentialid": 1, + "credentialprefix": "", + "enrolledvia": "auto", + "issuedat": "2026-01-01T00:00:00.000000Z", + "lastusedat": null, + "reenrollblocked": true, + "reenrollblockedreason": "rollback", + "revokedat": "2026-01-01T00:00:00.000000Z", + "revokedby": "testadmin" + }, + "message": "Endpoint credential revoked", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/endpoints/enrollment/migration", + "auth": "permission:endpoints.fleet", + "params": "not documented", + "purpose": "Per-computer readiness to move onto per-PC credentials, plus waves.", + "example": "curl http://localhost:5001/api/endpoints/enrollment/migration -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "state", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": { + "computers": [ + { + "agentversion": "1.19.0", + "assetid": 2, + "blockers": [], + "hostname": "", + "lastreportedat": "2026-01-01T00:00:00.000000Z", + "state": "ready" + }, + { + "agentversion": "1.19.0", + "assetid": 3, + "blockers": [], + "hostname": "", + "lastreportedat": "2026-01-01T00:00:00.000000Z", + "state": "ready" + }, + { + "agentversion": "1.20.0", + "assetid": 1, + "blockers": [ + "awaiting enrollment" + ], + "hostname": "", + "lastreportedat": "2026-01-01T00:00:00.000000Z", + "state": "blocked" + } + ], + "counts": { + "blocked": 1, + "enrolled": 0, + "notreporting": 0, + "pending": 0, + "ready": 2, + "upgrading": 0 + }, + "target": "1.20.0", + "waves": [ + { + "createdat": "2026-01-01T00:00:00Z", + "createdby": "", + "done": false, + "enrolled": 0, + "failed": 1, + "queued": 3, + "size": 3, + "waveid": 1 + }, + { + "createdat": "2026-01-01T00:00:00Z", + "createdby": "", + "done": false, + "enrolled": 0, + "failed": 0, + "queued": 2, + "size": 2, + "waveid": 2 + } + ] + }, + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 3, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/endpoints/enrollment/migration/wave", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Push the agent upgrade to a batch of READY PCs.", + "example": "curl -X POST http://localhost:5001/api/endpoints/enrollment/migration/wave -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "action": "run_script", + "target": "script:1" + }, + "responseexample": { + "data": { + "queued": [ + 1 + ], + "retried": [], + "skipped": [ + { + "assetid": 2, + "reason": "offline" + }, + { + "assetid": 3, + "reason": "notreporting" + }, + { + "assetid": 999999, + "reason": "not a computer" + } + ], + "waveid": 1 + }, + "message": "1 PC(s) queued", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/endpoints/enrollment/requests//approve", + "auth": "jwt + role:admin", + "params": "path: requestid (int); body: JSON (fields not yet documented)", + "purpose": "Bind a pending request to the asset the admin picked.", + "example": "curl -X POST http://localhost:5001/api/endpoints/enrollment/requests/1/approve -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "assetid": 1, + "sethostname": "" + }, + "responseexample": { + "data": { + "agentversion": "1.20.0", + "decidedat": "2026-01-01T00:00:00.000000Z", + "decidedby": "testadmin", + "deviceid": null, + "hostname": "", + "hostnameresult": "", + "lastseenat": "2026-01-01T00:00:00.000000Z", + "matchedassetid": 1, + "reason": null, + "requestedat": "2026-01-01T00:00:00.000000Z", + "requestid": "00000000", + "serialnumber": "", + "sourceip": "127.0.0.1", + "status": "approved" + }, + "message": "Enroll request approved; that computer was renamed to the hostname the PC reports", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/endpoints/enrollment/requests//reject", + "auth": "jwt + role:admin", + "params": "path: requestid (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: endpoints.reject_enroll_request has no docstring.", + "example": "curl -X POST http://localhost:5001/api/endpoints/enrollment/requests/1/reject -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "agentversion": "1.20.0", + "decidedat": "2026-01-01T00:00:00.000000Z", + "decidedby": "testadmin", + "deviceid": null, + "hostname": "", + "lastseenat": "2026-01-01T00:00:00.000000Z", + "matchedassetid": 1, + "reason": "awaiting approval", + "requestedat": "2026-01-01T00:00:00.000000Z", + "requestid": "00000000", + "serialnumber": "", + "sourceip": "127.0.0.1", + "status": "rejected" + }, + "message": "Enroll request rejected", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/endpoints/enrollment/settings", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Change the enrollment settings.", + "example": "curl -X PUT http://localhost:5001/api/endpoints/enrollment/settings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "revokeddeniessharedkey": true + }, + "responseexample": { + "data": { + "autoenroll": true, + "maxpendingenrollments": 500, + "revokeddeniessharedkey": false, + "sharedkeyallowed": false + }, + "message": "Enrollment settings saved", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/endpoints/logcollections/", + "auth": "permission:endpoints.updateapp", + "params": "path: collection_id (int)", + "purpose": "One collected log including its content, for the scrollable pane.", + "example": "curl http://localhost:5001/api/endpoints/logcollections/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/logcollections//download", + "auth": "permission:endpoints.updateapp", + "params": "path: collection_id (int)", + "purpose": "Serve a collected log as a file attachment.", + "example": "curl http://localhost:5001/api/endpoints/logcollections/1/download -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/monitors", + "auth": "permission:endpoints.view", + "params": "not documented", + "purpose": "List monitor definitions (shipped and local).", + "example": "curl http://localhost:5001/api/endpoints/monitors -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/endpoints/monitors", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a user-defined (local) monitor definition.", + "example": "curl -X POST http://localhost:5001/api/endpoints/monitors -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/endpoints/monitors/", + "auth": "jwt + role:admin", + "params": "path: monitor_id (int)", + "purpose": "Delete a local monitor definition. Shipped rows are protected.", + "example": "curl -X DELETE http://localhost:5001/api/endpoints/monitors/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/endpoints/monitors/", + "auth": "jwt + role:admin", + "params": "path: monitor_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a local monitor definition. Shipped rows are read-only.", + "example": "curl -X PUT http://localhost:5001/api/endpoints/monitors/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "launchpath": "D:\\Dnc\\bin\\DncMain.exe" + }, + "responseexample": { + "data": { + "createdat": "2026-01-01T00:00:00.000000Z", + "datacollectorpath": null, + "displayname": "", + "endpointmonitorid": 1, + "isactive": false, + "kind": "process", + "launchpath": "C:\\Program Files\\Dnc\\bin\\DncMain.exe", + "logcollectorpath": null, + "processname": "UDC", + "servicename": null, + "source": "shipped" + }, + "message": "Monitor updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/endpoints/monitors/resolve", + "auth": "permission:endpoints.view", + "params": "not documented", + "purpose": "Resolve the monitor set for a PC type (agent runtime fetch).", + "example": "curl http://localhost:5001/api/endpoints/monitors/resolve -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "pctype", + "type": "string", + "required": false + } ] - }, - { - "surface": "plugin-notifications", - "endpoints": [ - { - "method": "GET", - "path": "/api/notifications", - "auth": "permission:notifications.view", - "params": "not documented", - "purpose": "List all notifications with filtering and pagination.", - "example": "curl http://localhost:5001/api/notifications -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/monitors/resolved", + "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", + "params": "not documented", + "purpose": "Agent watch list: the monitors this endpoint should report on.", + "example": "curl http://localhost:5001/api/endpoints/monitors/resolved -H \"X-API-Key: $KEY\"", + "queryparams": [ + { + "name": "hostname", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": { + "processes": [ + { + "app": "Edge", + "launchpath": "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe", + "name": "msedge.exe" + } + ], + "services": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/endpoints/operatorreports", + "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", + "params": "body: JSON (fields not yet documented)", + "purpose": "Agent uploads one operator issue report (collector-token auth).", + "example": "curl -X POST http://localhost:5001/api/endpoints/operatorreports -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "assetid": 1, + "createdat": "2026-01-01T00:00:00.000000Z", + "details": "the CMM app will not start", + "hasimage": true, + "imagebytesize": 70, + "imagemimetype": "image/png", + "operatorreportid": 1, + "reason": "Other (free text)", + "reportedhostname": "" + }, + "message": "Operator report received", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/endpoints/operatorreports//image", + "auth": "permission:endpoints.operatorreports", + "params": "path: operatorreportid (int)", + "purpose": "Serve one operator report's screenshot.", + "example": "curl http://localhost:5001/api/endpoints/operatorreports/1/image -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/endpoints/panel//operatorreports", + "auth": "permission:endpoints.operatorreports", + "params": "path: asset_id (int)", + "purpose": "Operator Reports panel: this PC's recent operator issue reports.", + "example": "curl http://localhost:5001/api/endpoints/panel/1/operatorreports -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "reports": [ + { + "assetid": 1, + "createdat": "2026-01-01T00:00:00.000000Z", + "details": "second", + "hasimage": false, + "imagebytesize": null, + "imagemimetype": null, + "operatorreportid": 2, + "reason": "Other (free text)", + "reportedhostname": "" + }, + { + "assetid": 1, + "createdat": "2026-01-01T00:00:00.000000Z", + "details": "first", + "hasimage": false, + "imagebytesize": null, + "imagemimetype": null, + "operatorreportid": 1, + "reason": "Other (free text)", + "reportedhostname": "" + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/endpoints/scriptbody/", + "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)", + "params": "path: script_id (int)", + "purpose": "Agent-facing script body download (collector-token auth).", + "example": "curl http://localhost:5001/api/endpoints/scriptbody/1 -H \"X-API-Key: $KEY\"" + }, + { + "method": "GET", + "path": "/api/endpoints/settings/server-cert", + "auth": "jwt + role:admin", + "params": "not documented", + "purpose": "Get details about the current server certificate.", + "example": "curl http://localhost:5001/api/endpoints/settings/server-cert -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "expires_in_days": 29, + "is_self_signed": true, + "issuer": "CN = pin.example.test", + "not_after": "Oct 18 18:57:47 2026 GMT", + "not_before": "Sep 17 18:57:47 2026 GMT", + "san_list": [], + "sha1thumbprint": "", + "spkisha256": "", + "subject_cn": "pin.example.test", + "thumbprint": "99:F1:E1:1D:5A:66:EE:44:8C:CA:5C:D8:BF:EA:C3:F3:8F:82:40:D3:5E:C9:4D:F0:AC:0B:3C:AD:80:D7:C1:DF" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/endpoints/settings/server-cert/download", + "auth": "permission:endpoints.fleet", + "params": "not documented", + "purpose": "The PUBLIC server certificate as a DER .cer file, never the key.", + "example": "curl http://localhost:5001/api/endpoints/settings/server-cert/download -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/endpoints/settings/server-cert/generate", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Generate a new self-signed server certificate.", + "example": "curl -X POST http://localhost:5001/api/endpoints/settings/server-cert/generate -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "cn": "shopdb.example.test", + "days": 30, + "san": "" + }, + "responseexample": { + "data": { + "message": "Certificate generated successfully. Nginx is restarting." + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/endpoints/settings/server-cert/live", + "auth": "permission:endpoints.fleet", + "params": "not documented", + "purpose": "The certificate PCs actually receive from this site's public address.", + "example": "curl http://localhost:5001/api/endpoints/settings/server-cert/live -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "configured": true, + "error": "Could not read the certificate from shopdb.example.test:443 (Connection refused)", + "matchesfile": false, + "presentedsha1thumbprint": null, + "presentedspkisha256": null, + "url": "https://shopdb.example.test" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/endpoints/settings/server-cert/upload", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Upload a new server certificate and private key.", + "example": "curl -X POST http://localhost:5001/api/endpoints/settings/server-cert/upload -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "message": "Certificate uploaded successfully. Nginx is restarting." + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/endpoints/siteagent", + "auth": "permission:endpoints.agentpackage", + "params": "not documented", + "purpose": "Report whether the site agent build has a prebuilt binary staged.", + "example": "curl http://localhost:5001/api/endpoints/siteagent -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "agentcurrent": null, + "agentlegacy": null, + "agentstaged": false, + "agentversions": [], + "certavailable": true, + "serverpin": "", + "sharedkeyallowed": false, + "traycurrent": null, + "traylegacy": null, + "traystaged": false, + "trayversions": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/endpoints/siteagent/binaries/", + "auth": "permission:endpoints.agentpackage", + "params": "path: binkey (string)", + "purpose": "List staged versions for a binary (agent or tray), newest first.", + "example": "curl http://localhost:5001/api/endpoints/siteagent/binaries/x -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/endpoints/siteagent/binaries/", + "auth": "permission:endpoints.agentpackage", + "params": "path: binkey (string); body: JSON (fields not yet documented)", + "purpose": "Stage an uploaded binary as a new version.", + "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/x -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "binary": "agent", + "current": "", + "registration": { + "agentversion": "", + "appid": 1, + "appname": "ShopDB Endpoint Agent", + "contents": { + "agent": { + "filename": "shopdbagent.exe", + "sha256": "", + "version": "" + } + }, + "created": false, + "installpath": "/api/applications/package/1/shopdb-endpoint-agent.zip", + "ok": true, + "sha256": "", + "size": 135 }, - { - "method": "POST", - "path": "/api/notifications", - "auth": "permission:notifications.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new notification.", - "example": "curl -X POST http://localhost:5001/api/notifications -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "versions": [ + { + "iscurrent": true, + "size": 130, + "uploadedat": "2026-01-01T00:00:00.000000Z", + "version": "" + }, + { + "iscurrent": false, + "size": 130, + "uploadedat": "2026-01-01T00:00:00.000000Z", + "version": "" + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/endpoints/siteagent/binaries//adopt", + "auth": "permission:endpoints.agentpackage", + "params": "path: binkey (string); body: JSON (fields not yet documented)", + "purpose": "Adopt the unmanaged legacy flat file into the version store.", + "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/x/adopt -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "PUT", + "path": "/api/endpoints/siteagent/binaries//current", + "auth": "permission:endpoints.agentpackage", + "params": "path: binkey (string); body: JSON (fields not yet documented)", + "purpose": "Point 'current' at an already-staged version (roll back or forward).", + "example": "curl -X PUT http://localhost:5001/api/endpoints/siteagent/binaries/x/current -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "version": "" + }, + "responseexample": { + "data": { + "binary": "agent", + "current": "", + "registration": { + "agentversion": "", + "appid": 1, + "appname": "ShopDB Endpoint Agent", + "contents": { + "agent": { + "filename": "shopdbagent.exe", + "sha256": "", + "version": "" + } + }, + "created": false, + "installpath": "/api/applications/package/1/shopdb-endpoint-agent.zip", + "ok": true, + "sha256": "", + "size": 135 }, - { - "method": "DELETE", - "path": "/api/notifications/", - "auth": "permission:notifications.delete", - "params": "path: notification_id (int)", - "purpose": "Delete (soft delete) a notification.", - "example": "curl -X DELETE http://localhost:5001/api/notifications/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/notifications/", - "auth": "permission:notifications.view", - "params": "path: notification_id (int)", - "purpose": "Get a single notification.", - "example": "curl http://localhost:5001/api/notifications/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/notifications/", - "auth": "permission:notifications.edit", - "params": "path: notification_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a notification.", - "example": "curl -X PUT http://localhost:5001/api/notifications/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/notifications/active", - "auth": "permission:notifications.view", - "params": "not documented", - "purpose": "Get currently active notifications for display.", - "example": "curl http://localhost:5001/api/notifications/active -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/notifications/calendar", - "auth": "permission:notifications.view", - "params": "not documented", - "purpose": "Get notifications in FullCalendar event format.", - "example": "curl http://localhost:5001/api/notifications/calendar -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/notifications/dashboard/active", - "auth": "permission:notifications.view", - "params": "not documented", - "purpose": "Notifications currently showing on the floor.", - "example": "curl http://localhost:5001/api/notifications/dashboard/active -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/notifications/dashboard/summary", - "auth": "permission:notifications.view", - "params": "not documented", - "purpose": "Get notifications dashboard summary.", - "example": "curl http://localhost:5001/api/notifications/dashboard/summary -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/notifications/employee/", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "path: sso (string)", - "purpose": "Get recognitions for a specific employee by SSO.", - "example": "curl http://localhost:5001/api/notifications/employee/x" - }, - { - "method": "GET", - "path": "/api/notifications/shopfloor", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "not documented", - "purpose": "Get notifications for shopfloor TV dashboard.", - "example": "curl http://localhost:5001/api/notifications/shopfloor" - }, - { - "method": "GET", - "path": "/api/notifications/types", - "auth": "permission:notifications.view", - "params": "not documented", - "purpose": "List all notification types.", - "example": "curl http://localhost:5001/api/notifications/types -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/notifications/types", - "auth": "permission:notifications.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new notification type.", - "example": "curl -X POST http://localhost:5001/api/notifications/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "PATCH|PUT", - "path": "/api/notifications/types/", - "auth": "permission:notifications.create", - "params": "path: type_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a notification type, including its auto-expiry rule.", - "example": "curl -X PATCH http://localhost:5001/api/notifications/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "versions": [ + { + "iscurrent": false, + "size": 258, + "uploadedat": "2026-01-01T00:00:00.000000Z", + "version": "" + }, + { + "iscurrent": true, + "size": 258, + "uploadedat": "2026-01-01T00:00:00.000000Z", + "version": "" + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/endpoints/siteagent/binaries//detect", + "auth": "permission:endpoints.agentpackage", + "params": "path: binkey (string); body: JSON (fields not yet documented)", + "purpose": "Read the PE file version of an uploaded EXE without staging it.", + "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/x/detect -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/endpoints/siteagent/bootstrap.ps1", + "auth": "api-key (single-use bootstrap token in the X-Endpoint-Bootstrap-Token header, or ?t= while that is enabled; per-IP rate limited, 404 when not usable)", + "params": "not documented", + "purpose": "The PowerShell the one-line command downloads, runs and deletes.", + "example": "curl http://localhost:5001/api/endpoints/siteagent/bootstrap.ps1" + }, + { + "method": "GET", + "path": "/api/endpoints/siteagent/bootstrap/package", + "auth": "api-key (single-use bootstrap token in the X-Endpoint-Bootstrap-Token header, or ?t= while that is enabled; per-IP rate limited, 404 when not usable)", + "params": "not documented", + "purpose": "The keyless agent package the bootstrap script installs.", + "example": "curl http://localhost:5001/api/endpoints/siteagent/bootstrap/package" + }, + { + "method": "GET", + "path": "/api/endpoints/siteagent/bootstrap/tokens", + "auth": "jwt + role:admin", + "params": "not documented", + "purpose": "Every bootstrap token, newest first. Never a secret.", + "example": "curl http://localhost:5001/api/endpoints/siteagent/bootstrap/tokens -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "tokens": [ + { + "createdat": "2026-01-01T00:00:00.000000Z", + "createdby": "", + "expiresat": "2026-01-01T00:00:00.000000Z", + "lastusedat": null, + "lastusedip": null, + "maxuses": 25, + "remaining": 25, + "revokedat": null, + "state": "active", + "tokenid": 1, + "tokenprefix": "", + "usecount": 0 + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/endpoints/siteagent/bootstrap/tokens", + "auth": "jwt + role:admin", + "params": "body: JSON (fields not yet documented)", + "purpose": "Mint one short-lived bootstrap token. The secret is returned ONCE.", + "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/bootstrap/tokens -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "hours": 6, + "maxuses": 3 + }, + "responseexample": { + "data": { + "command": "powershell -ExecutionPolicy Bypass -c \"try { New-Item -ItemType Directory -Force -Path 'C:\\Program Files\\ShopDB\\bootstrap' | Out-Null; icacls 'C:\\Program Files\\ShopDB\\bootstrap' /inheritance:r /grant ...", + "scripturl": "https://shopdb.test/shopdb/api/endpoints/siteagent/bootstrap.ps1?t=&via=download", + "secret": "", + "token": { + "createdat": "2026-01-01T00:00:00.000000Z", + "createdby": "", + "expiresat": "2026-01-01T00:00:00.000000Z", + "lastusedat": null, + "lastusedip": null, + "maxuses": 500, + "remaining": 500, + "revokedat": null, + "state": "active", + "tokenid": 1, + "tokenprefix": "", + "usecount": 0 } - ] - }, - { - "surface": "plugin-printedparts", - "endpoints": [ - { - "method": "DELETE", - "path": "/api/printedparts/files/", - "auth": "permission:printedparts.delete", - "params": "path: file_id (int)", - "purpose": "Remove a bad revision (wrong file uploaded). History otherwise stays.", - "example": "curl -X DELETE http://localhost:5001/api/printedparts/files/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printedparts/files//download", - "auth": "permission:printedparts.view", - "params": "path: file_id (int)", - "purpose": "Download a revision under its original filename.", - "example": "curl http://localhost:5001/api/printedparts/files/1/download -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printedparts/image/", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "path: filename (path)", - "purpose": "Serve an uploaded item image (public - kiosk and list read it).", - "example": "curl http://localhost:5001/api/printedparts/image/example.png" - }, - { - "method": "GET", - "path": "/api/printedparts/items", - "auth": "permission:printedparts.view", - "params": "not documented", - "purpose": "List printed items, paginated; search + low-stock filter.", - "example": "curl http://localhost:5001/api/printedparts/items -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/printedparts/items", - "auth": "permission:printedparts.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a printed item.", - "example": "curl -X POST http://localhost:5001/api/printedparts/items -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/printedparts/items/", - "auth": "permission:printedparts.delete", - "params": "path: item_id (int)", - "purpose": "Soft-retire an item; its ledger history stays.", - "example": "curl -X DELETE http://localhost:5001/api/printedparts/items/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printedparts/items/", - "auth": "permission:printedparts.view", - "params": "path: item_id (int)", - "purpose": "Get one printed item with its recent transactions.", - "example": "curl http://localhost:5001/api/printedparts/items/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/printedparts/items/", - "auth": "permission:printedparts.edit", - "params": "path: item_id (int); body: JSON (fields not yet documented)", - "purpose": "Update catalog fields. Quantity moves ONLY through the ledger.", - "example": "curl -X PUT http://localhost:5001/api/printedparts/items/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/printedparts/items//adjust", - "auth": "permission:printedparts.restock", - "params": "path: item_id (int); body: JSON (fields not yet documented)", - "purpose": "Correct the count (damage, recount). Body: {quantitychange, reason, badge}.", - "example": "curl -X POST http://localhost:5001/api/printedparts/items/1/adjust -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/printedparts/items//files", - "auth": "permission:printedparts.view", - "params": "path: item_id (int)", - "purpose": "Revision history, newest first.", - "example": "curl http://localhost:5001/api/printedparts/items/1/files -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/printedparts/items//files", - "auth": "permission:printedparts.edit", - "params": "path: item_id (int); body: JSON (fields not yet documented)", - "purpose": "Upload the next revision of the item's print file.", - "example": "curl -X POST http://localhost:5001/api/printedparts/items/1/files -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/printedparts/items//image", - "auth": "permission:printedparts.delete", - "params": "path: item_id (int)", - "purpose": "Clear an item image; delete the file only if this plugin owns it.", - "example": "curl -X DELETE http://localhost:5001/api/printedparts/items/1/image -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/printedparts/items//image", - "auth": "permission:printedparts.edit", - "params": "path: item_id (int); body: JSON (fields not yet documented)", - "purpose": "Upload (or replace) the photo for an item (multipart file=).", - "example": "curl -X POST http://localhost:5001/api/printedparts/items/1/image -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/printedparts/items//restock", - "auth": "permission:printedparts.restock", - "params": "path: item_id (int); body: JSON (fields not yet documented)", - "purpose": "Add freshly printed stock. Body: {quantity, badge}.", - "example": "curl -X POST http://localhost:5001/api/printedparts/items/1/restock -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/printedparts/items//restore", - "auth": "permission:printedparts.delete", - "params": "path: item_id (int); body: JSON (fields not yet documented)", - "purpose": "Bring a retired item back; code, photo, and history are intact.", - "example": "curl -X POST http://localhost:5001/api/printedparts/items/1/restore -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/printedparts/kiosk/item/", - "auth": "api-key-optional (kiosk: the per-device token in the X-Kiosk-Token header, OR a caller address inside the site's kiosk CIDR allowlist - an allowlisted caller sends no credential; 404 while the site's kiosk flag is off)", - "params": "path: itemcode (string)", - "purpose": "Item summary for a scanned bin barcode. Kiosk-token gated.", - "example": "curl http://localhost:5001/api/printedparts/kiosk/item/x" - }, - { - "method": "GET", - "path": "/api/printedparts/kiosk/provisioning-link", - "auth": "permission:settings.edit", - "params": "not documented", - "purpose": "The URL that provisions a screen, token included, for an administrator.", - "example": "curl http://localhost:5001/api/printedparts/kiosk/provisioning-link -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/printedparts/kiosk/take", - "auth": "api-key-optional (kiosk: the per-device token in the X-Kiosk-Token header, OR a caller address inside the site's kiosk CIDR allowlist - an allowlisted caller sends no credential; 404 while the site's kiosk flag is off)", - "params": "body: JSON (fields not yet documented)", - "purpose": "Take parts from a bin. Body: {itemcode, badge, quantity}.", - "example": "curl -X POST http://localhost:5001/api/printedparts/kiosk/take -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/printedparts/reports/by-person", - "auth": "permission:printedparts.view", - "params": "not documented", - "purpose": "Takes grouped by employee, optionally bounded by ?days= (default 30).", - "example": "curl http://localhost:5001/api/printedparts/reports/by-person -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printedparts/reports/consumption", - "auth": "permission:printedparts.view", - "params": "not documented", - "purpose": "Takes per item, optionally bounded by ?days= (default 30).", - "example": "curl http://localhost:5001/api/printedparts/reports/consumption -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printedparts/reports/stock", - "auth": "permission:printedparts.view", - "params": "not documented", - "purpose": "Stock levels with low-stock flags and the cache-vs-ledger reconcile.", - "example": "curl http://localhost:5001/api/printedparts/reports/stock -H \"Authorization: Bearer $TOK\"" + }, + "message": "Bootstrap token created. The command is shown once.", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/endpoints/siteagent/bootstrap/tokens//revoke", + "auth": "jwt + role:admin", + "params": "path: tokenid (int); body: JSON (fields not yet documented)", + "purpose": "Kill a bootstrap token now. Already-revoked is a no-op, not an error.", + "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/bootstrap/tokens/1/revoke -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "token": { + "createdat": "2026-01-01T00:00:00.000000Z", + "createdby": "", + "expiresat": "2026-01-01T00:00:00.000000Z", + "lastusedat": null, + "lastusedip": null, + "maxuses": 25, + "remaining": 25, + "revokedat": "2026-01-01T00:00:00.000000Z", + "state": "revoked", + "tokenid": 1, + "tokenprefix": "", + "usecount": 0 } - ] - }, - { - "surface": "plugin-printers", - "endpoints": [ + }, + "message": "Bootstrap token revoked", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/endpoints/siteagent/package", + "auth": "permission:endpoints.agentpackage", + "params": "body: JSON (fields not yet documented)", + "purpose": "Bundle the site agent as a downloadable ZIP.", + "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/package -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "baseurl": "https://shopdb.test", + "bundlecert": true, + "cadenceseconds": 900, + "collectorkey": "", + "nextserverpin": "" + }, + "readsbody": true + } + ] + }, + { + "surface": "plugin-geenforce", + "endpoints": [ + { + "method": "GET", + "path": "/api/geenforce/applications", + "auth": "permission:geenforce.view or geenforce.manage", + "params": "not documented", + "purpose": "The core Applications catalog (id + name) for the curated entry->app link picker in the entry editor.", + "example": "curl http://localhost:5001/api/geenforce/applications -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/printers", - "auth": "permission:printers.view", - "params": "not documented", - "purpose": "List all printers with filtering and pagination.", - "example": "curl http://localhost:5001/api/printers -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/printers", - "auth": "permission:printers.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create new printer (creates both Asset and Printer records).", - "example": "curl -X POST http://localhost:5001/api/printers -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/printers/", - "auth": "permission:printers.delete", - "params": "path: printer_id (int)", - "purpose": "Delete (soft delete) printer.", - "example": "curl -X DELETE http://localhost:5001/api/printers/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printers/", - "auth": "permission:printers.view", - "params": "path: printer_id (int)", - "purpose": "Get a single printer with full details.", - "example": "curl http://localhost:5001/api/printers/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/printers/", - "auth": "permission:printers.edit", - "params": "path: printer_id (int); body: JSON (fields not yet documented)", - "purpose": "Update printer (both Asset and Printer records).", - "example": "curl -X PUT http://localhost:5001/api/printers/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/printers//supplies", - "auth": "permission:printers.view", - "params": "path: printer_id (int)", - "purpose": "Get supply levels from Zabbix (real-time lookup).", - "example": "curl http://localhost:5001/api/printers/1/supplies -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printers/assignments/for-asset/", - "auth": "permission:printers.view", - "params": "path: asset_id (int)", - "purpose": "What THIS asset is assigned, without inheritance.", - "example": "curl http://localhost:5001/api/printers/assignments/for-asset/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/printers/assignments/for-asset/", - "auth": "permission:printers.edit", - "params": "path: asset_id (int); body: JSON (fields not yet documented)", - "purpose": "Reconcile one asset's whole printer assignment in a single call.", - "example": "curl -X PUT http://localhost:5001/api/printers/assignments/for-asset/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/printers/assignments/seed-from-observed/", - "auth": "permission:printers.edit", - "params": "path: asset_id (int); body: JSON (fields not yet documented)", - "purpose": "Write what a PC observed as the assignment of the asset in the path.", - "example": "curl -X POST http://localhost:5001/api/printers/assignments/seed-from-observed/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/printers/by-asset/", - "auth": "permission:printers.view", - "params": "path: asset_id (int)", - "purpose": "Get printer data by asset ID.", - "example": "curl http://localhost:5001/api/printers/by-asset/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printers/dashboard/summary", - "auth": "permission:printers.view", - "params": "not documented", - "purpose": "Get printer dashboard summary data.", - "example": "curl http://localhost:5001/api/printers/dashboard/summary -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printers/dashboard/supplies", - "auth": "permission:printers.view", - "params": "not documented", - "purpose": "Printers needing a cartridge, flattened to one row per printer.", - "example": "curl http://localhost:5001/api/printers/dashboard/supplies -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printers/drivers", - "auth": "permission:printers.view", - "params": "not documented", - "purpose": "List printer drivers. ?active=false includes inactive ones.", - "example": "curl http://localhost:5001/api/printers/drivers -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/printers/drivers", - "auth": "permission:printers.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: printers_asset.create_driver has no docstring.", - "example": "curl -X POST http://localhost:5001/api/printers/drivers -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/printers/drivers/", - "auth": "permission:printers.delete", - "params": "path: driver_id (int)", - "purpose": "UNDOCUMENTED: printers_asset.delete_driver has no docstring.", - "example": "curl -X DELETE http://localhost:5001/api/printers/drivers/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/printers/drivers/", - "auth": "permission:printers.edit", - "params": "path: driver_id (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: printers_asset.update_driver has no docstring.", - "example": "curl -X PUT http://localhost:5001/api/printers/drivers/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/printers/for-host/", - "auth": "jwt-optional (works signed out; the handler decides what shows)", - "params": "path: hostname (string)", - "purpose": "Printers assigned to a PC, by hostname, with what it takes to install one.", - "example": "curl http://localhost:5001/api/printers/for-host/x -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printers/install-batch", - "auth": "jwt-optional (works signed out; the handler decides what shows)", - "params": "not documented", - "purpose": "Generate a Windows .bat that installs the selected printers.", - "example": "curl http://localhost:5001/api/printers/install-batch -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printers/install-list", - "auth": "jwt-optional (works signed out; the handler decides what shows)", - "params": "not documented", - "purpose": "Flat, unpaginated list of network printers for the printer installer.", - "example": "curl http://localhost:5001/api/printers/install-list -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printers/lookup", - "auth": "permission:printers.view", - "params": "not documented", - "purpose": "Find a printer by IP or FQDN. Parity with the classic printerlookup.asp.", - "example": "curl http://localhost:5001/api/printers/lookup -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printers/lowsupplies", - "auth": "permission:printers.view", - "params": "not documented", - "purpose": "Get printers with low or critical supply levels.", - "example": "curl http://localhost:5001/api/printers/lowsupplies -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printers/models", - "auth": "permission:printers.view", - "params": "not documented", - "purpose": "List models with a supply count, for the supply-management picker.", - "example": "curl http://localhost:5001/api/printers/models -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printers/models//supplies", - "auth": "permission:printers.view", - "params": "path: modelnumberid (int)", - "purpose": "List all supplies mapped to a model.", - "example": "curl http://localhost:5001/api/printers/models/1/supplies -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/printers/models//supplies", - "auth": "permission:printers.create", - "params": "path: modelnumberid (int); body: JSON (fields not yet documented)", - "purpose": "Add a supply to a model.", - "example": "curl -X POST http://localhost:5001/api/printers/models/1/supplies -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/printers/observed/", - "auth": "permission:printers.view", - "params": "path: hostname (string)", - "purpose": "What a host last reported, each queue judged against what it is assigned.", - "example": "curl http://localhost:5001/api/printers/observed/x -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printers/observed/for-asset/", - "auth": "permission:printers.view", - "params": "path: asset_id (int)", - "purpose": "The same comparison, reached from an asset page: one block per host.", - "example": "curl http://localhost:5001/api/printers/observed/for-asset/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printers/pc-default", - "auth": "jwt-optional (works signed out; the handler decides what shows)", - "params": "not documented", - "purpose": "Default printer for a PC, by machine (asset) number.", - "example": "curl http://localhost:5001/api/printers/pc-default -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "DELETE", - "path": "/api/printers/supplies/", - "auth": "permission:printers.delete", - "params": "path: modelsupplyid (int)", - "purpose": "Delete a model supply.", - "example": "curl -X DELETE http://localhost:5001/api/printers/supplies/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/printers/supplies/", - "auth": "permission:printers.edit", - "params": "path: modelsupplyid (int); body: JSON (fields not yet documented)", - "purpose": "Update a model supply.", - "example": "curl -X PUT http://localhost:5001/api/printers/supplies/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/printers/supplies/forecast", - "auth": "permission:printers.view", - "params": "not documented", - "purpose": "Days-to-empty and replacement counts per printer.", - "example": "curl http://localhost:5001/api/printers/supplies/forecast -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printers/supplies/meta", - "auth": "permission:printers.view", - "params": "not documented", - "purpose": "Allowed values for supply type, color, and capacity tier (for the UI).", - "example": "curl http://localhost:5001/api/printers/supplies/meta -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/printers/supplies/refresh", - "auth": "permission:printers.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Clear cached Zabbix supply data so the next read pulls fresh values.", - "example": "curl -X POST http://localhost:5001/api/printers/supplies/refresh -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/printers/types", - "auth": "permission:printers.view", - "params": "not documented", - "purpose": "List all printer types.", - "example": "curl http://localhost:5001/api/printers/types -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/printers/types", - "auth": "permission:printers.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Create a new printer type.", - "example": "curl -X POST http://localhost:5001/api/printers/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/printers/types/", - "auth": "permission:printers.delete", - "params": "path: type_id (int)", - "purpose": "Delete a printer type. Refused if any printer still uses it.", - "example": "curl -X DELETE http://localhost:5001/api/printers/types/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/printers/types/", - "auth": "permission:printers.view", - "params": "path: type_id (int)", - "purpose": "Get a single printer type.", - "example": "curl http://localhost:5001/api/printers/types/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/printers/types/", - "auth": "permission:printers.edit", - "params": "path: type_id (int); body: JSON (fields not yet documented)", - "purpose": "Update a printer type.", - "example": "curl -X PUT http://localhost:5001/api/printers/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "appid": 1, + "appname": "eDNC (tracked)" } - ] - }, - { - "surface": "plugin-slides", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/geenforce/config", + "auth": "permission:geenforce.view or geenforce.manage", + "params": "not documented", + "purpose": "Plugin config: the on-share export root + the client IP allowlist.", + "example": "curl http://localhost:5001/api/geenforce/config -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "allowedcidrs": "", + "shareroot": "" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/geenforce/config", + "auth": "permission:geenforce.publish", + "params": "body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: geenforce.put_config has no docstring.", + "example": "curl -X PUT http://localhost:5001/api/geenforce/config -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "shareroot": "" + }, + "responseexample": { + "data": { + "shareroot": "" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/geenforce/dashboard/failures", + "auth": "permission:geenforce.view or geenforce.manage", + "params": "not documented", + "purpose": "Entries that FAILED on their PC's most recent enforcement cycle.", + "example": "curl http://localhost:5001/api/geenforce/dashboard/failures -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/slides/", - "auth": "permission:slides.view or slides.manage", - "params": "path: surface (string)", - "purpose": "UNDOCUMENTED: slides.list_slides has no docstring.", - "example": "curl http://localhost:5001/api/slides/x -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PATCH", - "path": "/api/slides//", - "auth": "permission:slides.manage", - "params": "path: surface (string), slideid (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: slides.update_slide has no docstring.", - "example": "curl -X PATCH http://localhost:5001/api/slides/x/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/slides//delete", - "auth": "permission:slides.manage", - "params": "path: surface (string); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: slides.delete_slides has no docstring.", - "example": "curl -X POST http://localhost:5001/api/slides/x/delete -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/slides//order", - "auth": "permission:slides.manage", - "params": "path: surface (string); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: slides.reorder_slides has no docstring.", - "example": "curl -X POST http://localhost:5001/api/slides/x/order -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/slides//upload", - "auth": "permission:slides.manage", - "params": "path: surface (string); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: slides.upload_slides has no docstring.", - "example": "curl -X POST http://localhost:5001/api/slides/x/upload -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/slides/feed", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "not documented", - "purpose": "Flat playlist for a surface. NOT wrapped in the success_response envelope so the screensaver's existing parser needs no change.", - "example": "curl http://localhost:5001/api/slides/feed" - }, - { - "method": "GET", - "path": "/api/slides/img//", - "auth": "none (reviewed: named in the public-read or mutation sweep)", - "params": "path: surface (string), filename (path)", - "purpose": "Serve a slide image with a path-traversal guard.", - "example": "curl http://localhost:5001/api/slides/img/x/example.png" + "computerid": null, + "entryname": "Install OpenText", + "exitcode": 1603, + "hostname": "", + "message": "Fatal error during installation", + "receivedat": "2026-01-01T00:00:00Z", + "scopename": "gea-shopfloor-cmm" } - ] - }, - { - "surface": "plugin-tools", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "DELETE", + "path": "/api/geenforce/entries/", + "auth": "permission:geenforce.manage", + "params": "path: entryid (int)", + "purpose": "UNDOCUMENTED: geenforce.delete_entry has no docstring.", + "example": "curl -X DELETE http://localhost:5001/api/geenforce/entries/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "deleted": 2 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/geenforce/entries/", + "auth": "permission:geenforce.manage", + "params": "path: entryid (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: geenforce.update_entry has no docstring.", + "example": "curl -X PUT http://localhost:5001/api/geenforce/entries/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "InUseCheck": { + "Behavior": "CloseAndReopen", + "Processes": [ + { + "ExePath": "C:\\PCDLRN.exe", + "GracefulCloseTimeoutSec": 15, + "Name": "PCDLRN" + } + ] + }, + "Installer": "apps/pcdmis.msi", + "LogFile": "C:\\Logs\\pcdmis.log", + "Name": "PC-DMIS", + "PCTypesStrict": true, + "PreEnrollment": true, + "Type": "MSI" + }, + "responseexample": { + "data": { + "InUseCheck": { + "Behavior": "CloseAndReopen", + "Processes": [ + { + "ExePath": "C:\\PCDLRN.exe", + "GracefulCloseTimeoutSec": 15, + "Name": "PCDLRN" + } + ] + }, + "Installer": "apps/pcdmis.msi", + "LogFile": "C:\\Logs\\pcdmis.log", + "Name": "PC-DMIS", + "PCTypesStrict": true, + "PreEnrollment": true, + "Type": "MSI", + "appid": null, + "appname": null, + "entryid": 1, + "haspayload": false, + "payloadref": null, + "payloadsha256": null, + "payloadsource": "smb", + "sortorder": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/geenforce/entries//payload", + "auth": "permission:geenforce.view or geenforce.manage", + "params": "path: entryid (int)", + "purpose": "Return the stored inline payload bytes for an entry (404 if none).", + "example": "curl http://localhost:5001/api/geenforce/entries/1/payload -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/geenforce/entries//payload", + "auth": "permission:geenforce.publish", + "params": "path: entryid (int); body: JSON (fields not yet documented)", + "purpose": "Store an inline payload (<= 1 MB) for an entry and point the entry at it.", + "example": "curl -X POST http://localhost:5001/api/geenforce/entries/1/payload -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "Name": "eDNC install", + "PayloadRef": "config.reg", + "PayloadSha256": "", + "PayloadSource": "inline", + "Script": "scripts/x.ps1", + "Type": "PS1", + "appid": null, + "appname": null, + "entryid": 1, + "haspayload": true, + "payloadref": "config.reg", + "payloadsha256": "", + "payloadsource": "inline", + "sortorder": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/geenforce/manifest", + "auth": "api-key (geenforce fetch token, or the IP allowlist)", + "params": "not documented", + "purpose": "Serve the current published manifest for a scope (fat-client: full scope).", + "example": "curl http://localhost:5001/api/geenforce/manifest -H \"X-API-Key: $KEY\"", + "queryparams": [ + { + "name": "pctype", + "type": "string", + "required": false + }, + { + "name": "phase", + "type": "string", + "required": false + } + ], + "responseexample": { + "Version": "2.6", + "Applications": [ { - "method": "POST", - "path": "/api/tools/secrets", - "auth": "jwt-optional (works signed out; the handler decides what shows)", - "params": "body: JSON (fields not yet documented)", - "purpose": "Store an already-encrypted secret and return its token.", - "example": "curl -X POST http://localhost:5001/api/tools/secrets -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "Name": "Alpha", + "Type": "MSI", + "Installer": "apps/alpha.msi", + "DetectionMethod": "Registry", + "DetectionPath": "HKLM:\\SOFTWARE\\Alpha" }, { - "method": "GET", - "path": "/api/tools/secrets/", - "auth": "none (the unguessable token in the path is the authorisation)", - "params": "path: token (string)", - "purpose": "Whether a link is still live, WITHOUT spending it.", - "example": "curl http://localhost:5001/api/tools/secrets/x" + "Name": "Beta", + "Type": "PS1", + "Script": "scripts/beta.ps1", + "DetectionMethod": "Always" }, { - "method": "POST", - "path": "/api/tools/secrets/", - "auth": "none (the unguessable token in the path is the authorisation; redeeming spends the secret)", - "params": "path: token (string); body: JSON (fields not yet documented)", - "purpose": "Hand over the ciphertext once, then spend the view.", - "example": "curl -X POST http://localhost:5001/api/tools/secrets/x -H 'Content-Type: application/json' -d '{}'" + "Name": "Gamma", + "Type": "PS1", + "Script": "scripts/gamma.ps1" } - ] - }, - { - "surface": "plugin-usb", - "endpoints": [ + ] + } + }, + { + "method": "GET", + "path": "/api/geenforce/payload/", + "auth": "api-key (geenforce fetch token, or the IP allowlist)", + "params": "path: sha256 (string)", + "purpose": "Serve a payload blob by content hash over HTTPS.", + "example": "curl http://localhost:5001/api/geenforce/payload/x -H \"X-API-Key: $KEY\"" + }, + { + "method": "POST", + "path": "/api/geenforce/report", + "auth": "api-key (geenforce report token)", + "params": "body: JSON (fields not yet documented)", + "purpose": "Record one PC's enforcement cycle: applied version (did it receive the update?) + per-entry self-heal outcomes (installed/skipped/failed).", + "example": "curl -X POST http://localhost:5001/api/geenforce/report -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "appliedversion": 1, + "counts": { + "failed": 1, + "filtered": 0, + "installed": 2, + "skipped": 1 + }, + "enforcerversion": "2.6", + "hostname": "", + "phase": "runtime", + "results": [ { - "method": "GET", - "path": "/api/usb", - "auth": "permission:usb.view", - "params": "not documented", - "purpose": "List USB devices with checkout status.", - "example": "curl http://localhost:5001/api/usb -H \"Authorization: Bearer $TOK\"" + "action": "installed", + "exitcode": 0, + "message": "rule re-added", + "name": "VNC firewall rule", + "selfhealed": true }, { - "method": "POST", - "path": "/api/usb", - "auth": "permission:usb.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "Add a device (starts checked-in).", - "example": "curl -X POST http://localhost:5001/api/usb -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "action": "skipped", + "exitcode": 0, + "message": "", + "name": "PC-DMIS 2023.1", + "selfhealed": false }, { - "method": "GET", - "path": "/api/usb/", - "auth": "permission:usb.view", - "params": "path: device_id (string)", - "purpose": "Get a single device plus its last 20 check-in/out log rows.", - "example": "curl http://localhost:5001/api/usb/x -H \"Authorization: Bearer $TOK\"" + "action": "installed", + "exitcode": 0, + "name": "eDNC config", + "selfhealed": true }, + "" + ], + "scopename": "gea-shopfloor-cmm" + }, + "responseexample": { + "data": { + "reportid": 1, + "status": "selfhealed" + }, + "message": "recorded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/geenforce/reports", + "auth": "permission:geenforce.view or geenforce.manage", + "params": "not documented", + "purpose": "Latest enforcement report per PC (fleet compliance view).", + "example": "curl http://localhost:5001/api/geenforce/reports -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "hostname", + "type": "string", + "required": false + }, + { + "name": "scopename", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [ { - "method": "PUT", - "path": "/api/usb/", - "auth": "permission:usb.edit", - "params": "path: device_id (string); body: JSON (fields not yet documented)", - "purpose": "Edit device_desc / device_owner / locker_location / status.", - "example": "curl -X PUT http://localhost:5001/api/usb/x -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/usb//checkin", - "auth": "permission:usb.create", - "params": "path: device_id (string); body: JSON (fields not yet documented)", - "purpose": "Check a device back in from a badge.", - "example": "curl -X POST http://localhost:5001/api/usb/x/checkin -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/usb//checkout", - "auth": "permission:usb.create", - "params": "path: device_id (string); body: JSON (fields not yet documented)", - "purpose": "Check a device out to a badge.", - "example": "curl -X POST http://localhost:5001/api/usb/x/checkout -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/usb//history", - "auth": "permission:usb.view", - "params": "path: device_id (string)", - "purpose": "Paginated check-in/out log for one device.", - "example": "curl http://localhost:5001/api/usb/x/history -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/usb//retire", - "auth": "permission:usb.edit", - "params": "path: device_id (string); body: JSON (fields not yet documented)", - "purpose": "Retire a device (status -> retired).", - "example": "curl -X POST http://localhost:5001/api/usb/x/retire -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/usb/checkouts", - "auth": "permission:usb.view", - "params": "not documented", - "purpose": "List check-out log rows.", - "example": "curl http://localhost:5001/api/usb/checkouts -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/usb/checkouts/active", - "auth": "permission:usb.view", - "params": "not documented", - "purpose": "List the latest check-out log row for every currently checked-out device.", - "example": "curl http://localhost:5001/api/usb/checkouts/active -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/usb/kiosk/checkin", - "auth": "api-key-optional (kiosk: the per-device token in the X-Kiosk-Token header, OR a caller address inside the site's kiosk CIDR allowlist - an allowlisted caller sends no credential; 404 while the site's kiosk flag is off)", - "params": "body: JSON (fields not yet documented)", - "purpose": "Check a device in at the kiosk. Body: {device_id, badge, forsomeoneelse}.", - "example": "curl -X POST http://localhost:5001/api/usb/kiosk/checkin -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/usb/kiosk/checkout", - "auth": "api-key-optional (kiosk: the per-device token in the X-Kiosk-Token header, OR a caller address inside the site's kiosk CIDR allowlist - an allowlisted caller sends no credential; 404 while the site's kiosk flag is off)", - "params": "body: JSON (fields not yet documented)", - "purpose": "Check a device out at the kiosk. Body: {device_id, badge}.", - "example": "curl -X POST http://localhost:5001/api/usb/kiosk/checkout -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/usb/kiosk/device/", - "auth": "api-key-optional (kiosk: the per-device token in the X-Kiosk-Token header, OR a caller address inside the site's kiosk CIDR allowlist - an allowlisted caller sends no credential; 404 while the site's kiosk flag is off)", - "params": "path: device_id (string)", - "purpose": "What the kiosk shows after a device is scanned.", - "example": "curl http://localhost:5001/api/usb/kiosk/device/x" - }, - { - "method": "POST", - "path": "/api/usb/kiosk/others", - "auth": "api-key-optional (kiosk: the per-device token in the X-Kiosk-Token header, OR a caller address inside the site's kiosk CIDR allowlist - an allowlisted caller sends no credential; 404 while the site's kiosk flag is off)", - "params": "body: JSON (fields not yet documented)", - "purpose": "Drives checked out to OTHER people, with holder names. Body: {badge}.", - "example": "curl -X POST http://localhost:5001/api/usb/kiosk/others -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/usb/kiosk/provisioning-link", - "auth": "permission:settings.edit", - "params": "not documented", - "purpose": "The URL that provisions a screen, token included, for an administrator.", - "example": "curl http://localhost:5001/api/usb/kiosk/provisioning-link -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/usb/kiosk/session", - "auth": "api-key-optional (kiosk: the per-device token in the X-Kiosk-Token header, OR a caller address inside the site's kiosk CIDR allowlist - an allowlisted caller sends no credential; 404 while the site's kiosk flag is off)", - "params": "body: JSON (fields not yet documented)", - "purpose": "What one badge may do right now. Body: {badge}.", - "example": "curl -X POST http://localhost:5001/api/usb/kiosk/session -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/usb/kiosk/setup", - "auth": "setup-code (single-use kiosk setup code in ?code=, issued by the permission-gated provisioning link; spent on any attempt, 410 once used or expired)", - "params": "not documented", - "purpose": "Trade a setup code for this browser's kiosk cookie, then open the kiosk.", - "example": "curl http://localhost:5001/api/usb/kiosk/setup" - }, - { - "method": "GET", - "path": "/api/usb/reports/audit", - "auth": "permission:usb.view", - "params": "not documented", - "purpose": "Every check-out and check-in in the window, newest first.", - "example": "curl http://localhost:5001/api/usb/reports/audit -H \"Authorization: Bearer $TOK\"" + "appliedversion": null, + "assetnumber": "WJBAK10", + "backupcollectedat": "2026-01-01T00:00:00.000000Z", + "backupkind": "partmarker", + "backuplastseen": "2026-01-01T00:00:00.000000Z", + "backupok": false, + "backupstaleafterdays": 3, + "computerid": 1, + "displayrole": null, + "enforcerversion": null, + "failed": 0, + "filtered": 0, + "hostname": "", + "installed": 0, + "isstale": false, + "lastcheckin": null, + "latestversion": 1, + "levelid": null, + "location": null, + "machineassetid": null, + "machinenumber": null, + "machinepluginid": null, + "mapx": null, + "mapy": null, + "phase": "runtime", + "receivedat": "2026-01-01T00:00:00.000000Z", + "receivedlatest": false, + "reportid": 1, + "scopename": "gea-shopfloor-cmm", + "skipped": 0, + "staleafterminutes": 30, + "status": "ok", + "toolassetid": null, + "toolassetnumber": null, + "toolpluginid": null } - ] - }, - { - "surface": "plugin-warranty", - "endpoints": [ + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/geenforce/reports/", + "auth": "permission:geenforce.view or geenforce.manage", + "params": "path: reportid (int)", + "purpose": "One report with per-entry outcomes (self-heal / failure detail).", + "example": "curl http://localhost:5001/api/geenforce/reports/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "appliedversion": 1, + "hostname": "", + "latestversion": 1, + "phase": "runtime", + "receivedlatest": true, + "reportid": 1, + "results": [ + { + "action": "installed", + "entryname": "VNC firewall rule", + "exitcode": 0, + "message": "rule re-added", + "selfhealed": true + }, + { + "action": "skipped", + "entryname": "PC-DMIS 2023.1", + "exitcode": 0, + "message": "", + "selfhealed": false + }, + { + "action": "installed", + "entryname": "eDNC config", + "exitcode": 0, + "message": null, + "selfhealed": true + }, + "" + ], + "scopename": "gea-shopfloor-cmm", + "status": "failed" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/geenforce/scopes", + "auth": "permission:geenforce.view or geenforce.manage", + "params": "not documented", + "purpose": "List imaging PC-type scopes with entry + published-version counts.", + "example": "curl http://localhost:5001/api/geenforce/scopes -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ { - "method": "GET", - "path": "/api/warranty", - "auth": "permission:warranty.view", - "params": "not documented", - "purpose": "List warranties. Filters: ?status=, ?assetid=, ?active=false.", - "example": "curl http://localhost:5001/api/warranty -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/warranty", - "auth": "permission:warranty.create", - "params": "body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: warranty.create_warranty has no docstring.", - "example": "curl -X POST http://localhost:5001/api/warranty -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/warranty/", - "auth": "permission:warranty.delete", - "params": "path: warrantyid (int)", - "purpose": "UNDOCUMENTED: warranty.delete_warranty has no docstring.", - "example": "curl -X DELETE http://localhost:5001/api/warranty/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/warranty/", - "auth": "permission:warranty.view", - "params": "path: warrantyid (int)", - "purpose": "UNDOCUMENTED: warranty.get_warranty has no docstring.", - "example": "curl http://localhost:5001/api/warranty/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "PUT", - "path": "/api/warranty/", - "auth": "permission:warranty.edit", - "params": "path: warrantyid (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: warranty.update_warranty has no docstring.", - "example": "curl -X PUT http://localhost:5001/api/warranty/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "DELETE", - "path": "/api/warranty//proof", - "auth": "permission:warranty.edit", - "params": "path: warrantyid (int)", - "purpose": "Remove a warranty's proof document.", - "example": "curl -X DELETE http://localhost:5001/api/warranty/1/proof -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/warranty//proof", - "auth": "permission:warranty.edit", - "params": "path: warrantyid (int); body: JSON (fields not yet documented)", - "purpose": "Upload (or replace) the proof-of-cover document for a warranty.", - "example": "curl -X POST http://localhost:5001/api/warranty/1/proof -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "POST", - "path": "/api/warranty//refresh", - "auth": "permission:warranty.edit", - "params": "path: warrantyid (int); body: JSON (fields not yet documented)", - "purpose": "UNDOCUMENTED: warranty.refresh_warranty has no docstring.", - "example": "curl -X POST http://localhost:5001/api/warranty/1/refresh -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" - }, - { - "method": "GET", - "path": "/api/warranty/asset/", - "auth": "permission:warranty.view", - "params": "path: assetid (int)", - "purpose": "Warranties covering one asset (for the asset-detail panel).", - "example": "curl http://localhost:5001/api/warranty/asset/1 -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/warranty/dashboard/expiring", - "auth": "permission:warranty.view", - "params": "not documented", - "purpose": "Warranties running out, and ones that already have.", - "example": "curl http://localhost:5001/api/warranty/dashboard/expiring -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/warranty/proof/", - "auth": "permission:warranty.view", - "params": "path: filename (path)", - "purpose": "Download a proof document.", - "example": "curl http://localhost:5001/api/warranty/proof/example.png -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "GET", - "path": "/api/warranty/report", - "auth": "permission:warranty.view", - "params": "not documented", - "purpose": "Counts + lists bucketed by derived status.", - "example": "curl http://localhost:5001/api/warranty/report -H \"Authorization: Bearer $TOK\"" - }, - { - "method": "POST", - "path": "/api/warranty/sync/dell", - "auth": "permission:warranty.edit", - "params": "body: JSON (fields not yet documented)", - "purpose": "Look up every asset serial (= Dell service tag) that has no warranty yet and create warranties for the ones Dell recognizes.", - "example": "curl -X POST http://localhost:5001/api/warranty/sync/dell -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + "computertypeid": null, + "entrycount": 2, + "iscommon": false, + "manifestversion": "2.6", + "measuringtooltypeid": null, + "minlibversion": null, + "phase": "runtime", + "publishedversion": 1, + "scopeid": 1, + "scopename": "gea-shopfloor-cmm" } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/geenforce/scopes", + "auth": "permission:geenforce.manage", + "params": "body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: geenforce.create_scope has no docstring.", + "example": "curl -X POST http://localhost:5001/api/geenforce/scopes -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "description": "test scope", + "phase": "runtime", + "scopename": "gea-shopfloor-collections" + }, + "responseexample": { + "data": { + "computertypeid": null, + "description": "test scope", + "entrycount": 0, + "iscommon": false, + "manifestversion": "1.0", + "measuringtooltypeid": null, + "phase": "runtime", + "publishedversion": null, + "scopeid": 1, + "scopename": "gea-shopfloor-collections" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/geenforce/scopes/", + "auth": "permission:geenforce.manage", + "params": "path: scopeid (int)", + "purpose": "UNDOCUMENTED: geenforce.delete_scope has no docstring.", + "example": "curl -X DELETE http://localhost:5001/api/geenforce/scopes/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "deleted": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/geenforce/scopes/", + "auth": "permission:geenforce.view or geenforce.manage", + "params": "path: scopeid (int)", + "purpose": "UNDOCUMENTED: geenforce.get_scope has no docstring.", + "example": "curl http://localhost:5001/api/geenforce/scopes/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "computertypeid": null, + "description": "test scope", + "entries": [ + { + "Name": "Beta", + "Script": "scripts/b.ps1", + "Type": "PS1", + "appid": null, + "appname": null, + "entryid": 2, + "haspayload": false, + "payloadref": null, + "payloadsha256": null, + "payloadsource": "smb", + "sortorder": 0 + }, + { + "DetectionMethod": "FileVersion", + "DetectionPath": "C:\\a.exe", + "DetectionValue": "1.0", + "Installer": "apps/a.exe", + "Name": "Alpha", + "Type": "EXE", + "appid": null, + "appname": null, + "entryid": 1, + "haspayload": false, + "payloadref": null, + "payloadsha256": null, + "payloadsource": "smb", + "sortorder": 1 + } + ], + "entrycount": 2, + "iscommon": false, + "manifestversion": "1.0", + "measuringtooltypeid": null, + "phase": "runtime", + "publishedversion": null, + "scopeid": 1, + "scopename": "gea-shopfloor-test" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/geenforce/scopes/", + "auth": "permission:geenforce.manage", + "params": "path: scopeid (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: geenforce.update_scope has no docstring.", + "example": "curl -X PUT http://localhost:5001/api/geenforce/scopes/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "computertypeid": 3, + "description": "updated" + }, + "responseexample": { + "data": { + "computertypeid": 3, + "description": "updated", + "entrycount": 0, + "iscommon": false, + "manifestversion": "1.0", + "measuringtooltypeid": null, + "phase": "runtime", + "publishedversion": null, + "scopeid": 1, + "scopename": "gea-shopfloor-test" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/geenforce/scopes//compliance", + "auth": "permission:geenforce.view or geenforce.manage", + "params": "path: scopeid (int)", + "purpose": "Fleet-install coverage per app-linked entry (from collected PC data).", + "example": "curl http://localhost:5001/api/geenforce/scopes/1/compliance -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "computersplugin": false, + "phase": "runtime", + "rows": [ + { + "appid": 1, + "appname": "PC-DMIS", + "coveragenote": "computers plugin not installed; install counts unavailable", + "entryid": 1, + "entryname": "PC-DMIS 2019", + "expectedversion": "2019 R1", + "installedcount": null, + "versionmatchcount": null + } + ], + "scopeid": 1, + "scopename": "gea-shopfloor-cmm" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/geenforce/scopes//entries", + "auth": "permission:geenforce.manage", + "params": "path: scopeid (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: geenforce.create_entry has no docstring.", + "example": "curl -X POST http://localhost:5001/api/geenforce/scopes/1/entries -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "InUseCheck": { + "Behavior": "CloseAndReopen", + "Processes": [ + { + "ExePath": "C:\\PCDLRN.exe", + "GracefulCloseTimeoutSec": 15, + "Name": "PCDLRN" + } + ] + }, + "Installer": "apps/pcdmis.msi", + "LogFile": "C:\\Logs\\pcdmis.log", + "Name": "PC-DMIS", + "PCTypesStrict": true, + "PreEnrollment": true, + "Type": "MSI" + }, + "responseexample": { + "data": { + "InUseCheck": { + "Behavior": "CloseAndReopen", + "Processes": [ + { + "ExePath": "C:\\PCDLRN.exe", + "GracefulCloseTimeoutSec": 15, + "Name": "PCDLRN" + } + ] + }, + "Installer": "apps/pcdmis.msi", + "LogFile": "C:\\Logs\\pcdmis.log", + "Name": "PC-DMIS", + "PCTypesStrict": true, + "PreEnrollment": true, + "Type": "MSI", + "appid": null, + "appname": null, + "entryid": 1, + "haspayload": false, + "payloadref": null, + "payloadsha256": null, + "payloadsource": "smb", + "sortorder": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "PUT", + "path": "/api/geenforce/scopes//entries/reorder", + "auth": "permission:geenforce.manage", + "params": "path: scopeid (int); body: JSON (fields not yet documented)", + "purpose": "Set entry order from a list of entryids (the ordering contract).", + "example": "curl -X PUT http://localhost:5001/api/geenforce/scopes/1/entries/reorder -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "order": [ + 2, + 1 + ] + }, + "responseexample": { + "data": { + "order": [ + 2, + 1 + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/geenforce/scopes//export-share", + "auth": "permission:geenforce.publish", + "params": "path: scopeid (int); body: JSON (fields not yet documented)", + "purpose": "Write the scope's current published JSON to the configured share root (backing up the old file to _meta/history first).", + "example": "curl -X POST http://localhost:5001/api/geenforce/scopes/1/export-share -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "path": "" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/geenforce/scopes//preview", + "auth": "permission:geenforce.view or geenforce.manage", + "params": "path: scopeid (int)", + "purpose": "Render the DRAFT manifest JSON a publish would freeze (review before ship).", + "example": "curl http://localhost:5001/api/geenforce/scopes/1/preview -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "manifest": { + "Applications": [ + { + "DetectionMethod": "Registry", + "DetectionPath": "HKLM:\\SOFTWARE\\Alpha", + "Installer": "apps/alpha.msi", + "Name": "Alpha", + "Type": "MSI" + }, + { + "DetectionMethod": "Always", + "Name": "Beta", + "Script": "scripts/beta.ps1", + "Type": "PS1" + } + ], + "Version": "2.6" + }, + "scopename": "gea-shopfloor-cmm" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/geenforce/scopes//publish", + "auth": "permission:geenforce.publish", + "params": "path: scopeid (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: geenforce.publish_scope_route has no docstring.", + "example": "curl -X POST http://localhost:5001/api/geenforce/scopes/1/publish -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "notes": "v1" + }, + "responseexample": { + "data": { + "versionnumber": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/geenforce/scopes//publish-preflight", + "auth": "permission:geenforce.view or geenforce.manage", + "params": "path: scopeid (int)", + "purpose": "Would publishing this scope outrun the fleet's enforcer lib?", + "example": "curl http://localhost:5001/api/geenforce/scopes/1/publish-preflight -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/geenforce/scopes//rollback", + "auth": "permission:geenforce.publish", + "params": "path: scopeid (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: geenforce.rollback_scope_route has no docstring.", + "example": "curl -X POST http://localhost:5001/api/geenforce/scopes/1/rollback -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "versionnumber": 1 + }, + "responseexample": { + "data": { + "versionnumber": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/geenforce/scopes//simulate", + "auth": "permission:geenforce.view or geenforce.manage", + "params": "path: scopeid (int)", + "purpose": "Which draft entries apply to a given machine profile, and why the rest are filtered out (reuses the engine-mirror filters).", + "example": "curl http://localhost:5001/api/geenforce/scopes/1/simulate -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "cmmversion", + "type": "string", + "required": false + }, + { + "name": "hostname", + "type": "string", + "required": false + }, + { + "name": "machinenumber", + "type": "string", + "required": false + }, + { + "name": "pctype", + "type": "string", + "required": false + }, + { + "name": "subtype", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": { + "applied": [ + "CLM (untagged)", + "PC-DMIS 2019" + ], + "filtered": [ + { + "filteredby": [ + "_CmmVersion" + ], + "name": "PC-DMIS 2016" + } + ], + "profile": { + "cmmversion": "2019", + "hostname": "", + "machinenumber": null, + "pctype": "gea-shopfloor-cmm", + "phase": "runtime", + "subtype": null + } + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/geenforce/scopes//versions", + "auth": "permission:geenforce.view or geenforce.manage", + "params": "path: scopeid (int)", + "purpose": "UNDOCUMENTED: geenforce.list_versions has no docstring.", + "example": "curl http://localhost:5001/api/geenforce/scopes/1/versions -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ + { + "iscurrent": false, + "notes": "v2", + "publishedat": "2026-01-01T00:00:00.000000Z", + "publishedby": 1, + "versionnumber": 2 + }, + { + "iscurrent": true, + "notes": "v1", + "publishedat": "2026-01-01T00:00:00.000000Z", + "publishedby": 1, + "versionnumber": 1 + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/geenforce/scopes//versions/", + "auth": "permission:geenforce.view or geenforce.manage", + "params": "path: scopeid (int), versionnumber (int)", + "purpose": "UNDOCUMENTED: geenforce.get_version has no docstring.", + "example": "curl http://localhost:5001/api/geenforce/scopes/1/versions/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "manifest": { + "Applications": [ + { + "Name": "Alpha", + "Type": "MSI" + }, + { + "Name": "Beta", + "Type": "PS1" + } + ], + "Version": "1.0" + }, + "versionnumber": 2 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + } + ] + }, + { + "surface": "plugin-guides", + "endpoints": [ + { + "method": "GET", + "path": "/api/guides", + "auth": "permission:guides.view", + "params": "not documented", + "purpose": "Published guides, plus the caller's own drafts if they can author.", + "example": "curl http://localhost:5001/api/guides -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "q", + "type": "string", + "required": false + }, + { + "name": "tag", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [ + { + "canedit": false, + "checklistitems": 0, + "createdat": "2026-01-01T00:00:00.000000Z", + "guideid": 1, + "ispublished": true, + "publishedat": "2026-01-01T00:00:00.000000Z", + "revision": 2, + "slug": "published-procedure", + "status": "published", + "summary": null, + "tags": [], + "title": "Published procedure", + "updatedat": "2026-01-01T00:00:00.000000Z" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/guides", + "auth": "permission:guides.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: guides.create_guide has no docstring.", + "example": "curl -X POST http://localhost:5001/api/guides -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "blocks": [], + "title": "Replacing a marker controler" + }, + "responseexample": { + "data": { + "blocks": [], + "createdat": "2026-01-01T00:00:00.000000Z", + "guideid": 1, + "publishedat": null, + "revision": 1, + "slug": "replacing-a-marker-controler", + "status": "draft", + "summary": null, + "tags": [], + "title": "Replacing a marker controler", + "updatedat": "2026-01-01T00:00:00.000000Z" + }, + "message": "Guide created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/guides/", + "auth": "permission:guides.delete", + "params": "path: guideid (int)", + "purpose": "UNDOCUMENTED: guides.delete_guide has no docstring.", + "example": "curl -X DELETE http://localhost:5001/api/guides/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/guides/", + "auth": "permission:guides.view", + "params": "path: guideid (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: guides.update_guide has no docstring.", + "example": "curl -X PUT http://localhost:5001/api/guides/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "blocks": [], + "title": "Replacing a marker controller" + }, + "responseexample": { + "data": { + "blocks": [], + "createdat": "2026-01-01T00:00:00.000000Z", + "guideid": 1, + "publishedat": null, + "revision": 2, + "saved": true, + "slug": "replacing-a-marker-controler", + "status": "draft", + "summary": null, + "tags": [], + "title": "Replacing a marker controller", + "updatedat": "2026-01-01T00:00:00.000000Z" + }, + "message": "Saved", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/guides//publish", + "auth": "permission:guides.publish", + "params": "path: guideid (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: guides.publish_guide has no docstring.", + "example": "curl -X POST http://localhost:5001/api/guides/1/publish -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + }, + { + "method": "POST", + "path": "/api/guides//restore/", + "auth": "permission:guides.view", + "params": "path: guideid (int), revisionid (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: guides.restore_revision has no docstring.", + "example": "curl -X POST http://localhost:5001/api/guides/1/restore/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + }, + { + "method": "GET", + "path": "/api/guides//revisions", + "auth": "permission:guides.view", + "params": "path: guideid (int)", + "purpose": "UNDOCUMENTED: guides.list_revisions has no docstring.", + "example": "curl http://localhost:5001/api/guides/1/revisions -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/guides//unpublish", + "auth": "permission:guides.publish", + "params": "path: guideid (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: guides.unpublish_guide has no docstring.", + "example": "curl -X POST http://localhost:5001/api/guides/1/unpublish -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + }, + { + "method": "GET", + "path": "/api/guides//working", + "auth": "permission:guides.view", + "params": "path: guideid (int)", + "purpose": "The working copy, for the editor.", + "example": "curl http://localhost:5001/api/guides/1/working -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/guides/", + "auth": "permission:guides.view", + "params": "path: slug (string)", + "purpose": "What a reader gets: the published revision.", + "example": "curl http://localhost:5001/api/guides/x -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/guides/images", + "auth": "permission:guides.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Take a pasted or dropped image.", + "example": "curl -X POST http://localhost:5001/api/guides/images -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "bytesize": 40, + "createdat": "2026-01-01T00:00:00.000000Z", + "filename": "screenshot.png", + "guideid": null, + "guideimageid": 1, + "mimetype": "image/png", + "url": "/api/guides/images/1" + }, + "message": "Image uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/guides/images/", + "auth": "permission:guides.view", + "params": "path: imageid (int)", + "purpose": "The image itself, under the same rule as the guide that holds it.", + "example": "curl http://localhost:5001/api/guides/images/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/guides/revisions/", + "auth": "permission:guides.view", + "params": "path: revisionid (int)", + "purpose": "One revision by id.", + "example": "curl http://localhost:5001/api/guides/revisions/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "blocks": [ + { + "id": "b1", + "text": "secret draft wording", + "type": "paragraph" + } + ], + "createdat": "2026-01-01T00:00:00.000000Z", + "createdby": "", + "guideid": 1, + "guiderevisionid": 1, + "note": null, + "revision": 1, + "shorthash": "", + "summary": null, + "tags": [], + "title": "Published procedure" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/guides/tags", + "auth": "permission:guides.view", + "params": "not documented", + "purpose": "Every tag in use, with a count, for the filter chips.", + "example": "curl http://localhost:5001/api/guides/tags -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + } + ] + }, + { + "surface": "plugin-knowledgebase", + "endpoints": [ + { + "method": "GET", + "path": "/api/knowledgebase", + "auth": "permission:kb.view", + "params": "not documented", + "purpose": "List all knowledge base articles.", + "example": "curl http://localhost:5001/api/knowledgebase -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "appid", + "type": "integer", + "required": false + }, + { + "name": "linkurl", + "type": "string", + "required": false + }, + { + "name": "order", + "type": "string", + "required": false, + "default": "desc" + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "shortdescription", + "type": "string", + "required": false + }, + { + "name": "sort", + "type": "string", + "required": false, + "default": "clicks" + } + ], + "responseexample": { + "data": [ + { + "appid": 1, + "application": { + "appid": 1, + "appname": "Spotfire" + }, + "clicks": 0, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "keywords": "install", + "lastupdated": "2026-01-01T00:00:00.000000Z", + "linkid": 3, + "linkurl": "https://kb.example/c", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "shortdescription": "Spotfire install guide" + }, + { + "appid": 1, + "application": { + "appid": 1, + "appname": "Spotfire" + }, + "clicks": 0, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "keywords": "export", + "lastupdated": "2026-01-01T00:00:00.000000Z", + "linkid": 2, + "linkurl": "https://kb.example/b", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "shortdescription": "Export a visualization" + }, + { + "appid": 1, + "application": { + "appid": 1, + "appname": "Spotfire" + }, + "clicks": 0, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "keywords": "login", + "lastupdated": "2026-01-01T00:00:00.000000Z", + "linkid": 1, + "linkurl": "https://kb.example/a", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "shortdescription": "Dashboard login steps" + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 3, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/knowledgebase", + "auth": "permission:kb.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new knowledge base article.", + "example": "curl -X POST http://localhost:5001/api/knowledgebase -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "keywords": "printer reset", + "linkurl": "https://kb.example/printer-reset", + "shortdescription": "How to reset a printer" + }, + "responseexample": { + "data": { + "appid": null, + "clicks": 0, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "keywords": "printer reset", + "lastupdated": "2026-01-01T00:00:00.000000Z", + "linkid": 1, + "linkurl": "https://kb.example/printer-reset", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "shortdescription": "How to reset a printer" + }, + "message": "Article created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/knowledgebase/", + "auth": "permission:kb.delete", + "params": "path: link_id (int)", + "purpose": "Delete (deactivate) a knowledge base article.", + "example": "curl -X DELETE http://localhost:5001/api/knowledgebase/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/knowledgebase/", + "auth": "permission:kb.view", + "params": "path: link_id (int)", + "purpose": "Get a single knowledge base article.", + "example": "curl http://localhost:5001/api/knowledgebase/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "appid": null, + "application": null, + "clicks": 0, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "keywords": "printer reset", + "lastupdated": "2026-01-01T00:00:00.000000Z", + "linkid": 1, + "linkurl": "https://kb.example/printer-reset", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "shortdescription": "How to reset a printer" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/knowledgebase/", + "auth": "permission:kb.edit", + "params": "path: link_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a knowledge base article.", + "example": "curl -X PUT http://localhost:5001/api/knowledgebase/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "POST", + "path": "/api/knowledgebase//click", + "auth": "permission:kb.view", + "params": "path: link_id (int); body: JSON (fields not yet documented)", + "purpose": "Increment the click counter for an article the caller is allowed to read.", + "example": "curl -X POST http://localhost:5001/api/knowledgebase/1/click -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + }, + { + "method": "GET", + "path": "/api/knowledgebase/stats", + "auth": "permission:kb.view", + "params": "not documented", + "purpose": "Get knowledge base statistics.", + "example": "curl http://localhost:5001/api/knowledgebase/stats -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "totalarticles": 0, + "totalclicks": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + } + ] + }, + { + "surface": "plugin-machines", + "endpoints": [ + { + "method": "GET", + "path": "/api/machines", + "auth": "permission:machines.view", + "params": "not documented", + "purpose": "List all machines with filtering and pagination.", + "example": "curl http://localhost:5001/api/machines -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "assetnumber", + "type": "string", + "required": false + }, + { + "name": "businessunit_id", + "type": "integer", + "required": false + }, + { + "name": "businessunitid", + "type": "integer", + "required": false + }, + { + "name": "dir", + "type": "string", + "required": false, + "default": "asc" + }, + { + "name": "location_id", + "type": "integer", + "required": false + }, + { + "name": "locationid", + "type": "integer", + "required": false + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "sort", + "type": "string", + "required": false, + "default": "assetnumber" + }, + { + "name": "type_id", + "type": "integer", + "required": false + }, + { + "name": "typeid", + "type": "integer", + "required": false + }, + { + "name": "vendor_id", + "type": "integer", + "required": false + }, + { + "name": "vendorid", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": [ + { + "assetid": 1, + "assetnumber": "0410", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "machine", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "dualpathpartner": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "machine": { + "assetid": 1, + "controllermodelid": null, + "controllervendorid": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "islocationonly": false, + "lastmaintenancedate": null, + "machineid": 1, + "machinetypeid": 1, + "machinetypename": "Part Washer", + "maintenanceintervaldays": null, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextmaintenancedate": null, + "requiresmanualconfig": false, + "vendorid": 1, + "vendorname": "Acme Industrial" + }, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Cell 4 washer", + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/machines", + "auth": "permission:machines.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create new machine (creates both Asset and Machine records).", + "example": "curl -X POST http://localhost:5001/api/machines -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "assetnumber": "MAINT-DATE-1", + "lastmaintenancedate": "2026-01-01", + "name": "Maintenance date probe", + "nextmaintenancedate": "2026-01-01" + }, + "responseexample": { + "data": { + "assetid": 1, + "assetnumber": "MAINT-DATE-1", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "machine", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "machine": { + "assetid": 1, + "controllermodelid": null, + "controllervendorid": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "islocationonly": false, + "lastmaintenancedate": "2026-01-01", + "machineid": 1, + "machinetypeid": null, + "maintenanceintervaldays": null, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextmaintenancedate": "2026-01-01", + "requiresmanualconfig": false, + "vendorid": null + }, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Maintenance date probe", + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 + }, + "message": "Machine created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/machines/", + "auth": "permission:machines.delete", + "params": "path: machine_id (int)", + "purpose": "Delete (soft delete) machine.", + "example": "curl -X DELETE http://localhost:5001/api/machines/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/machines/", + "auth": "permission:machines.view", + "params": "path: machine_id (int)", + "purpose": "Get a single machine item with full details.", + "example": "curl http://localhost:5001/api/machines/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "assetid": 1, + "assetnumber": "2001", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "machine", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "dualpathpartner": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "machine": { + "assetid": 1, + "controllermodelid": null, + "controllervendorid": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "imageurl": "/api/models/images/model-1.png", + "isactive": true, + "islocationonly": false, + "lastmaintenancedate": null, + "machineid": 1, + "machinetypeid": null, + "maintenanceintervaldays": null, + "modelname": "IMG-MODEL-1", + "modelnumberid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextmaintenancedate": null, + "requiresmanualconfig": false, + "vendorid": null + }, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/machines/", + "auth": "permission:machines.edit", + "params": "path: machine_id (int); body: JSON (fields not yet documented)", + "purpose": "Update machine (both Asset and Machine records).", + "example": "curl -X PUT http://localhost:5001/api/machines/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/machines/by-asset/", + "auth": "permission:machines.view", + "params": "path: asset_id (int)", + "purpose": "Get machine data by asset ID.", + "example": "curl http://localhost:5001/api/machines/by-asset/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/machines/dashboard/outofservice", + "auth": "permission:machines.view", + "params": "not documented", + "purpose": "Machines whose status says they are not running.", + "example": "curl http://localhost:5001/api/machines/dashboard/outofservice -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/machines/dashboard/summary", + "auth": "permission:machines.view", + "params": "not documented", + "purpose": "Get machine dashboard summary data.", + "example": "curl http://localhost:5001/api/machines/dashboard/summary -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "bystatus": [ + { + "count": 1, + "status": "In Use" + } + ], + "bytype": [], + "total": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/machines/types", + "auth": "permission:machines.view", + "params": "not documented", + "purpose": "List all machine types.", + "example": "curl http://localhost:5001/api/machines/types -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/machines/types", + "auth": "permission:machines.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new machine type.", + "example": "curl -X POST http://localhost:5001/api/machines/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/machines/types/", + "auth": "permission:machines.delete", + "params": "path: type_id (int)", + "purpose": "Delete a machine type. Refused if any asset still uses it.", + "example": "curl -X DELETE http://localhost:5001/api/machines/types/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/machines/types/", + "auth": "permission:machines.view", + "params": "path: type_id (int)", + "purpose": "Get a single machine type.", + "example": "curl http://localhost:5001/api/machines/types/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/machines/types/", + "auth": "permission:machines.edit", + "params": "path: type_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a machine type.", + "example": "curl -X PUT http://localhost:5001/api/machines/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + } + ] + }, + { + "surface": "plugin-measuringtools", + "endpoints": [ + { + "method": "GET", + "path": "/api/measuringtools", + "auth": "permission:measuringtools.view", + "params": "not documented", + "purpose": "List measuring tools with filters + pagination.", + "example": "curl http://localhost:5001/api/measuringtools -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "assetnumber", + "type": "string", + "required": false + }, + { + "name": "calibrationstatus", + "type": "string", + "required": false + }, + { + "name": "locationid", + "type": "integer", + "required": false + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "statusid", + "type": "integer", + "required": false + }, + { + "name": "typeid", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": [ + { + "assetid": 26, + "assetnumber": "MT-PAGE-05", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "measuring_tool", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "measuringtool": { + "assetid": 26, + "calibrationintervaldays": null, + "calibrationprovider": null, + "calibrationstatus": "overdue", + "calibrationstatuscolor": "#F44336", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "lastcalibrationdate": null, + "measuringtoolid": 26, + "measuringtooltypeid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextcalibrationdate": "2026-01-01", + "notes": null + }, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Paged tool 5", + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 26, + "serialnumber": "", + "statuscolor": null, + "statusid": 1, + "statusname": "In Use" + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 2, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/measuringtools", + "auth": "permission:measuringtools.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a measuring tool: one Asset core row + one extension row.", + "example": "curl -X POST http://localhost:5001/api/measuringtools -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "assetnumber": "MT-001", + "calibrationintervaldays": 365, + "calibrationprovider": "Metro Cal Lab", + "gaugelabreference": "GL-42", + "lastcalibrationdate": "2026-01-01", + "measuringtooltypeid": 1, + "name": "Bench caliper", + "nextcalibrationdate": "2026-01-01", + "serialnumber": "", + "statusid": 1 + }, + "responseexample": { + "data": { + "assetid": 1, + "assetnumber": "MT-001", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "measuring_tool", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": "GL-42", + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "measuringtool": { + "assetid": 1, + "calibrationintervaldays": 365, + "calibrationprovider": "Metro Cal Lab", + "calibrationstatus": "current", + "calibrationstatuscolor": "#4CAF50", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "lastcalibrationdate": "2026-01-01", + "measuringtoolid": 1, + "measuringtooltypecolor": "#14abef", + "measuringtooltypeid": 1, + "measuringtooltypename": "Caliper", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextcalibrationdate": "2026-01-01", + "notes": null + }, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Bench caliper", + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statuscolor": null, + "statusid": 1, + "statusname": "In Use" + }, + "message": "Measuring tool created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/measuringtools/", + "auth": "permission:measuringtools.delete", + "params": "path: tool_id (int)", + "purpose": "Soft delete a measuring tool (deactivates its asset).", + "example": "curl -X DELETE http://localhost:5001/api/measuringtools/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "message": "Measuring tool deleted", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/measuringtools/", + "auth": "permission:measuringtools.view", + "params": "path: tool_id (int)", + "purpose": "Get one measuring tool (asset core merged with the extension).", + "example": "curl http://localhost:5001/api/measuringtools/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "assetid": 1, + "assetnumber": "MT-001", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "measuring_tool", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": "GL-42", + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "measuringtool": { + "assetid": 1, + "calibrationintervaldays": 365, + "calibrationprovider": "Metro Cal Lab", + "calibrationstatus": "current", + "calibrationstatuscolor": "#4CAF50", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "lastcalibrationdate": "2026-01-01", + "measuringtoolid": 1, + "measuringtooltypecolor": "#14abef", + "measuringtooltypeid": 1, + "measuringtooltypename": "Caliper", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextcalibrationdate": "2026-01-01", + "notes": null + }, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Bench caliper", + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statuscolor": null, + "statusid": 1, + "statusname": "In Use" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/measuringtools/", + "auth": "permission:measuringtools.edit", + "params": "path: tool_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a measuring tool (asset core fields + extension in one payload).", + "example": "curl -X PUT http://localhost:5001/api/measuringtools/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "calibrationprovider": "In-house", + "name": "Renamed tool", + "nextcalibrationdate": "2026-01-01" + }, + "responseexample": { + "data": { + "assetid": 20, + "assetnumber": "MT-AUDIT-1", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "measuring_tool", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "measuringtool": { + "assetid": 20, + "calibrationintervaldays": null, + "calibrationprovider": null, + "calibrationstatus": "current", + "calibrationstatuscolor": "#4CAF50", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "lastcalibrationdate": "2026-01-01", + "measuringtoolid": 20, + "measuringtooltypeid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextcalibrationdate": "2026-01-01", + "notes": null + }, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "After rename", + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 20, + "serialnumber": "", + "statuscolor": null, + "statusid": 1, + "statusname": "In Use" + }, + "message": "Measuring tool updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/measuringtools/by-asset/", + "auth": "permission:measuringtools.view", + "params": "path: asset_id (int)", + "purpose": "Get a measuring tool by its core asset id.", + "example": "curl http://localhost:5001/api/measuringtools/by-asset/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/measuringtools/map-overlay", + "auth": "permission:measuringtools.view", + "params": "not documented", + "purpose": "Calibration-status overlay for active measuring tools.", + "example": "curl http://localhost:5001/api/measuringtools/map-overlay -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ + { + "assetid": 4, + "color": "#F44336", + "label": "Overdue" + }, + { + "assetid": 6, + "color": "#F44336", + "label": "Overdue" + }, + { + "assetid": 16, + "color": "#F44336", + "label": "Overdue" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/measuringtools/report/calibration", + "auth": "permission:measuringtools.view", + "params": "not documented", + "purpose": "Counts + lists bucketed by derived calibration status.", + "example": "curl http://localhost:5001/api/measuringtools/report/calibration -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "buckets": { + "current": [], + "duesoon": [], + "overdue": [], + "unknown": [] + }, + "counts": { + "current": 0, + "duesoon": 0, + "overdue": 0, + "unknown": 0 + }, + "statuscolors": { + "current": "#4CAF50", + "duesoon": "#FF9800", + "overdue": "#F44336", + "unknown": "#9E9E9E" + } + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/measuringtools/types", + "auth": "permission:measuringtools.view", + "params": "not documented", + "purpose": "List measuring-tool types.", + "example": "curl http://localhost:5001/api/measuringtools/types -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [ + { + "color": "#7934f3", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "Bore / hole diameter gage", + "isactive": true, + "measuringtooltypeid": 4, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Bore Gage" + }, + { + "color": "#14abef", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "Vernier / digital caliper", + "isactive": true, + "measuringtooltypeid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Caliper" + }, + { + "color": "#e83e8c", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "Reference gage block set", + "isactive": true, + "measuringtooltypeid": 7, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Gage Block Set" + }, + "" + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 8, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/measuringtools/types", + "auth": "permission:measuringtools.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a measuring-tool type (reactivates a soft-deleted same-named one).", + "example": "curl -X POST http://localhost:5001/api/measuringtools/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "color": "#123456", + "description": "Pin gage set", + "name": "Pin Gage" + }, + "responseexample": { + "data": { + "color": "#123456", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "Pin gage set", + "isactive": true, + "measuringtooltypeid": 9, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Pin Gage" + }, + "message": "Measuring-tool type created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/measuringtools/types/", + "auth": "permission:measuringtools.delete", + "params": "path: type_id (int)", + "purpose": "Delete a measuring-tool type. Refused if any tool still uses it.", + "example": "curl -X DELETE http://localhost:5001/api/measuringtools/types/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "message": "Measuring-tool type deleted", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/measuringtools/types/", + "auth": "permission:measuringtools.view", + "params": "path: type_id (int)", + "purpose": "Get one measuring-tool type.", + "example": "curl http://localhost:5001/api/measuringtools/types/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/measuringtools/types/", + "auth": "permission:measuringtools.edit", + "params": "path: type_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a measuring-tool type.", + "example": "curl -X PUT http://localhost:5001/api/measuringtools/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "description": "Updated" + }, + "responseexample": { + "data": { + "color": "#123456", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "Updated", + "isactive": true, + "measuringtooltypeid": 9, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Pin Gage" + }, + "message": "Measuring-tool type updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + } + ] + }, + { + "surface": "plugin-network", + "endpoints": [ + { + "method": "GET", + "path": "/api/network", + "auth": "permission:network.view", + "params": "not documented", + "purpose": "List all network devices with filtering and pagination.", + "example": "curl http://localhost:5001/api/network -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "assetnumber", + "type": "string", + "required": false + }, + { + "name": "businessunit_id", + "type": "integer", + "required": false + }, + { + "name": "businessunitid", + "type": "integer", + "required": false + }, + { + "name": "dir", + "type": "string", + "required": false, + "default": "asc" + }, + { + "name": "location_id", + "type": "integer", + "required": false + }, + { + "name": "locationid", + "type": "integer", + "required": false + }, + { + "name": "managed", + "type": "string", + "required": false + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "poe", + "type": "string", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "sort", + "type": "string", + "required": false, + "default": "hostname" + }, + { + "name": "type_id", + "type": "integer", + "required": false + }, + { + "name": "typeid", + "type": "integer", + "required": false + }, + { + "name": "vendor_id", + "type": "integer", + "required": false + }, + { + "name": "vendorid", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": [ + { + "assetid": 1, + "assetnumber": "NET-1", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "network_device", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "ipaddress": "", + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "networkdevice": { + "assetid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "firmwareversion": null, + "hostname": "", + "isactive": true, + "ismanaged": false, + "ispoe": false, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "networkdeviceid": 1, + "networkdevicetypeid": 1, + "networkdevicetypename": "Switch", + "portcount": null, + "rackunit": null, + "vendorid": 1, + "vendorname": "Cisco" + }, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/network", + "auth": "permission:network.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create new network device (creates both Asset and NetworkDevice records).", + "example": "curl -X POST http://localhost:5001/api/network -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "assetnumber": "SW-CORE-01", + "hostname": "", + "ipaddress": "", + "name": "Core switch" + }, + "responseexample": { + "data": { + "assetid": 1, + "assetnumber": "APNL-Dock-Door-4", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "network_device", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "ipaddress": "", + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Dock Door 4", + "networkdevice": { + "assetid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "firmwareversion": null, + "hostname": "", + "isactive": true, + "ismanaged": false, + "ispoe": false, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "networkdeviceid": 1, + "networkdevicetypeid": 1, + "networkdevicetypename": "Access Panel", + "portcount": null, + "rackunit": null, + "vendorid": null + }, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 + }, + "message": "Network device created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/network/", + "auth": "permission:network.delete", + "params": "path: device_id (int)", + "purpose": "Delete (soft delete) network device.", + "example": "curl -X DELETE http://localhost:5001/api/network/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/network/", + "auth": "permission:network.view", + "params": "path: device_id (int)", + "purpose": "Get a single network device with full details.", + "example": "curl http://localhost:5001/api/network/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "assetid": 1, + "assetnumber": "AST-networkdevice", + "assettypecolor": null, + "assettypeid": 3, + "assettypename": "network_device", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": "GL-2", + "ipaddress": "", + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": "MNT-2", + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "networkdevice": { + "assetid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "firmwareversion": null, + "hostname": "", + "isactive": true, + "ismanaged": false, + "ispoe": false, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "networkdeviceid": 1, + "networkdevicetypeid": null, + "portcount": null, + "rackunit": null, + "vendorid": null + }, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/network/", + "auth": "permission:network.edit", + "params": "path: device_id (int); body: JSON (fields not yet documented)", + "purpose": "Update network device (both Asset and NetworkDevice records).", + "example": "curl -X PUT http://localhost:5001/api/network/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "gaugelabreference": "GL-2", + "maintenancereference": "MNT-2" + }, + "responseexample": { + "data": { + "assetid": 1, + "assetnumber": "AST-networkdevice", + "assettypecolor": null, + "assettypeid": 3, + "assettypename": "network_device", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": "GL-2", + "ipaddress": "", + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": "MNT-2", + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "networkdevice": { + "assetid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "firmwareversion": null, + "hostname": "", + "isactive": true, + "ismanaged": false, + "ispoe": false, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "networkdeviceid": 1, + "networkdevicetypeid": null, + "portcount": null, + "rackunit": null, + "vendorid": null + }, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 + }, + "message": "Network device updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/network/by-asset/", + "auth": "permission:network.view", + "params": "path: asset_id (int)", + "purpose": "Get network device data by asset ID.", + "example": "curl http://localhost:5001/api/network/by-asset/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/network/by-hostname/", + "auth": "permission:network.view", + "params": "path: hostname (string)", + "purpose": "Get network device by hostname.", + "example": "curl http://localhost:5001/api/network/by-hostname/x -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/network/dashboard/summary", + "auth": "permission:network.view", + "params": "not documented", + "purpose": "Get network device dashboard summary data.", + "example": "curl http://localhost:5001/api/network/dashboard/summary -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "bytype": [], + "byvendor": [], + "nonpoe": 0, + "poe": 0, + "total": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/network/subnets", + "auth": "permission:network.view", + "params": "not documented", + "purpose": "List all subnets with filtering and pagination.", + "example": "curl http://localhost:5001/api/network/subnets -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "locationid", + "type": "integer", + "required": false + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "type", + "type": "string", + "required": false + }, + { + "name": "vlanid", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/network/subnets", + "auth": "permission:network.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new subnet.", + "example": "curl -X POST http://localhost:5001/api/network/subnets -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "cidr": "", + "name": "Attached", + "vlanid": 1 + }, + "responseexample": { + "data": { + "broadcastaddress": null, + "cidr": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "dhcpenabled": true, + "dhcprangeend": null, + "dhcprangestart": null, + "dns1": null, + "dns2": null, + "gatewayip": null, + "isactive": true, + "locationid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Attached", + "networkaddress": null, + "subnetid": 1, + "subnetmask": null, + "subnettype": null, + "vlanid": 1, + "vlanname": "In use", + "vlannumber": 77 + }, + "message": "Subnet created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/network/subnets/", + "auth": "permission:network.delete", + "params": "path: subnet_id (int)", + "purpose": "Delete (soft delete) a subnet.", + "example": "curl -X DELETE http://localhost:5001/api/network/subnets/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/network/subnets/", + "auth": "permission:network.view", + "params": "path: subnet_id (int)", + "purpose": "Get a single subnet plus the network devices whose primary IP falls in its CIDR range.", + "example": "curl http://localhost:5001/api/network/subnets/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/network/subnets/", + "auth": "permission:network.edit", + "params": "path: subnet_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a subnet.", + "example": "curl -X PUT http://localhost:5001/api/network/subnets/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/network/types", + "auth": "permission:network.view", + "params": "not documented", + "purpose": "List all network device types.", + "example": "curl http://localhost:5001/api/network/types -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/network/types", + "auth": "permission:network.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new network device type.", + "example": "curl -X POST http://localhost:5001/api/network/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "networkdevicetype": "Door Controller", + "prefix": "DOOR" + }, + "responseexample": { + "data": { + "color": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "icon": null, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "networkdevicetype": "Door Controller", + "networkdevicetypeid": 1, + "prefix": "DOOR" + }, + "message": "Network device type created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/network/types/", + "auth": "permission:network.delete", + "params": "path: type_id (int)", + "purpose": "Delete a network device type. Refused if any device still uses it.", + "example": "curl -X DELETE http://localhost:5001/api/network/types/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/network/types/", + "auth": "permission:network.view", + "params": "path: type_id (int)", + "purpose": "Get a single network device type.", + "example": "curl http://localhost:5001/api/network/types/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/network/types/", + "auth": "permission:network.edit", + "params": "path: type_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a network device type.", + "example": "curl -X PUT http://localhost:5001/api/network/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "prefix": "DR" + }, + "responseexample": { + "data": { + "color": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "icon": null, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "networkdevicetype": "Door Controller", + "networkdevicetypeid": 1, + "prefix": "DR" + }, + "message": "Network device type updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/network/vlans", + "auth": "permission:network.view", + "params": "not documented", + "purpose": "List all VLANs with filtering and pagination.", + "example": "curl http://localhost:5001/api/network/vlans -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "type", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/network/vlans", + "auth": "permission:network.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new VLAN.", + "example": "curl -X POST http://localhost:5001/api/network/vlans -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "name": "VLAN 4094", + "vlannumber": 4094 + }, + "responseexample": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "VLAN 4094", + "subnetcount": 0, + "vlanid": 1, + "vlannumber": 4094, + "vlantype": null + }, + "message": "VLAN created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/network/vlans/", + "auth": "permission:network.delete", + "params": "path: vlan_id (int)", + "purpose": "Delete (soft delete) a VLAN.", + "example": "curl -X DELETE http://localhost:5001/api/network/vlans/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/network/vlans/", + "auth": "permission:network.view", + "params": "path: vlan_id (int)", + "purpose": "Get a single VLAN with its subnets.", + "example": "curl http://localhost:5001/api/network/vlans/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/network/vlans/", + "auth": "permission:network.edit", + "params": "path: vlan_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a VLAN.", + "example": "curl -X PUT http://localhost:5001/api/network/vlans/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + } + ] + }, + { + "surface": "plugin-notifications", + "endpoints": [ + { + "method": "GET", + "path": "/api/notifications", + "auth": "permission:notifications.view", + "params": "not documented", + "purpose": "List all notifications with filtering and pagination.", + "example": "curl http://localhost:5001/api/notifications -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "current", + "type": "string", + "required": false, + "default": "false" + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "ticketnumber", + "type": "string", + "required": false + }, + { + "name": "type_id", + "type": "integer", + "required": false + }, + { + "name": "typeid", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/notifications", + "auth": "permission:notifications.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new notification.", + "example": "curl -X POST http://localhost:5001/api/notifications -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "alertfleet": true, + "alertscope": { + "computertype": "CMM PC", + "hostname": [ + "", + "" + ] + }, + "notification": "Line 3 is down", + "notificationtypeid": 1 + }, + "responseexample": { + "data": { + "alertfleet": true, + "alertscope": { + "computertype": "CMM PC", + "hostname": [ + "", + "" + ] + }, + "alerttimeout": 0, + "appid": null, + "businessunitid": null, + "employeename": "", + "employeesso": "", + "enddate": null, + "endtime": null, + "isactive": true, + "iscurrent": true, + "isshopfloor": false, + "link": null, + "linkurl": null, + "message": "Line 3 is down", + "notification": "Line 3 is down", + "notificationid": 1, + "notificationtypeid": 1, + "startdate": "2026-01-01T00:00:00.000000+00:00", + "starttime": "2026-01-01T00:00:00.000000+00:00", + "ticketnumber": null, + "title": "Line 3 is down", + "typecolor": "#d93f3f", + "typename": "Fleet Alert" + }, + "message": "Notification created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/notifications/", + "auth": "permission:notifications.delete", + "params": "path: notification_id (int)", + "purpose": "Delete (soft delete) a notification.", + "example": "curl -X DELETE http://localhost:5001/api/notifications/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/notifications/", + "auth": "permission:notifications.view", + "params": "path: notification_id (int)", + "purpose": "Get a single notification.", + "example": "curl http://localhost:5001/api/notifications/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/notifications/", + "auth": "permission:notifications.edit", + "params": "path: notification_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a notification.", + "example": "curl -X PUT http://localhost:5001/api/notifications/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "alertfleet": null, + "alerttimeout": null + }, + "responseexample": { + "data": { + "alertfleet": false, + "alertscope": null, + "alerttimeout": 0, + "appid": null, + "businessunitid": null, + "employeename": "", + "employeesso": "", + "enddate": null, + "endtime": null, + "isactive": true, + "iscurrent": true, + "isshopfloor": false, + "link": null, + "linkurl": null, + "message": "Line 3 is down", + "notification": "Line 3 is down", + "notificationid": 1, + "notificationtypeid": 1, + "startdate": "2026-01-01T00:00:00.000000+00:00", + "starttime": "2026-01-01T00:00:00.000000+00:00", + "ticketnumber": null, + "title": "Line 3 is down", + "typecolor": "#d93f3f", + "typename": "Fleet Alert" + }, + "message": "Notification updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/notifications/active", + "auth": "permission:notifications.view", + "params": "not documented", + "purpose": "Get currently active notifications for display.", + "example": "curl http://localhost:5001/api/notifications/active -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "notifications": [], + "total": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/notifications/calendar", + "auth": "permission:notifications.view", + "params": "not documented", + "purpose": "Get notifications in FullCalendar event format.", + "example": "curl http://localhost:5001/api/notifications/calendar -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "end", + "type": "string", + "required": false + }, + { + "name": "start", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/notifications/dashboard/active", + "auth": "permission:notifications.view", + "params": "not documented", + "purpose": "Notifications currently showing on the floor.", + "example": "curl http://localhost:5001/api/notifications/dashboard/active -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/notifications/dashboard/summary", + "auth": "permission:notifications.view", + "params": "not documented", + "purpose": "Get notifications dashboard summary.", + "example": "curl http://localhost:5001/api/notifications/dashboard/summary -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "active": 0, + "bytype": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/notifications/employee/", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "path: sso (string)", + "purpose": "Get recognitions for a specific employee by SSO.", + "example": "curl http://localhost:5001/api/notifications/employee/x" + }, + { + "method": "GET", + "path": "/api/notifications/shopfloor", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "not documented", + "purpose": "Get notifications for shopfloor TV dashboard.", + "example": "curl http://localhost:5001/api/notifications/shopfloor", + "queryparams": [ + { + "name": "businessunit", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": { + "configversion": "2b7c2d2f543f", + "current": [ + { + "boardcategory": "Current Events", + "boardorder": 50, + "displaystyle": "grid", + "employeename": "", + "employeepicture": "", + "employeesso": "", + "endtime": null, + "isactive": true, + "isshopfloor": true, + "link": null, + "notification": "press down", + "notificationid": 2, + "resolved": false, + "starttime": null, + "ticketnumber": null, + "typecolor": "#17a2b8", + "typename": "Incident" + }, + { + "boardcategory": "Current Events", + "boardorder": 30, + "displaystyle": "grid", + "employeename": "", + "employeepicture": "", + "employeesso": "", + "endtime": null, + "isactive": true, + "isshopfloor": true, + "link": null, + "notification": "retooling", + "notificationid": 1, + "resolved": false, + "starttime": null, + "ticketnumber": null, + "typecolor": "#17a2b8", + "typename": "Change" + } + ], + "timestamp": "2026-01-01T00:00:00.000000", + "upcoming": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/notifications/types", + "auth": "permission:notifications.view", + "params": "not documented", + "purpose": "List all notification types.", + "example": "curl http://localhost:5001/api/notifications/types -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": [ + { + "boardcategory": "Alerts", + "boardorder": 100, + "displaystyle": "grid", + "expirydays": null, + "expiryhour": null, + "expiryminute": 0, + "expirymode": "none", + "gracewindowminutes": 0, + "isactive": true, + "notificationtypeid": 1, + "showemployeephoto": "", + "splitperemployee": "", + "typecolor": "#17a2b8", + "typedescription": null, + "typename": "Awareness" + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/notifications/types", + "auth": "permission:notifications.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new notification type.", + "example": "curl -X POST http://localhost:5001/api/notifications/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "boardcategory": "Alerts", + "displaystyle": "grid", + "typename": "Awareness" + }, + "responseexample": { + "data": { + "boardcategory": "Alerts", + "boardorder": 100, + "displaystyle": "grid", + "expirydays": null, + "expiryhour": null, + "expiryminute": 0, + "expirymode": "none", + "gracewindowminutes": 0, + "isactive": true, + "notificationtypeid": 1, + "showemployeephoto": "", + "splitperemployee": "", + "typecolor": "#17a2b8", + "typedescription": null, + "typename": "Awareness" + }, + "message": "Notification type created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "PATCH|PUT", + "path": "/api/notifications/types/", + "auth": "permission:notifications.create", + "params": "path: type_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a notification type, including its auto-expiry rule.", + "example": "curl -X PATCH http://localhost:5001/api/notifications/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "boardcategory": "Alerts" + }, + "responseexample": { + "data": { + "boardcategory": "Alerts", + "boardorder": 100, + "displaystyle": "grid", + "expirydays": null, + "expiryhour": null, + "expiryminute": 0, + "expirymode": "none", + "gracewindowminutes": 0, + "isactive": true, + "notificationtypeid": 1, + "showemployeephoto": "", + "splitperemployee": "", + "typecolor": "#17a2b8", + "typedescription": null, + "typename": "Awareness" + }, + "message": "Notification type updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + } + ] + }, + { + "surface": "plugin-printedparts", + "endpoints": [ + { + "method": "DELETE", + "path": "/api/printedparts/files/", + "auth": "permission:printedparts.delete", + "params": "path: file_id (int)", + "purpose": "Remove a bad revision (wrong file uploaded). History otherwise stays.", + "example": "curl -X DELETE http://localhost:5001/api/printedparts/files/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/printedparts/files//download", + "auth": "permission:printedparts.view", + "params": "path: file_id (int)", + "purpose": "Download a revision under its original filename.", + "example": "curl http://localhost:5001/api/printedparts/files/1/download -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/printedparts/image/", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "path: filename (path)", + "purpose": "Serve an uploaded item image (public - kiosk and list read it).", + "example": "curl http://localhost:5001/api/printedparts/image/example.png" + }, + { + "method": "GET", + "path": "/api/printedparts/items", + "auth": "permission:printedparts.view", + "params": "not documented", + "purpose": "List printed items, paginated; search + low-stock filter.", + "example": "curl http://localhost:5001/api/printedparts/items -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "lowstock", + "type": "string", + "required": false, + "default": "" + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [ + { + "binlocation": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "gagelabtag": "WJRP0117", + "imageurl": null, + "isactive": true, + "islowstock": true, + "itemcode": "3DP0001", + "itemdescription": null, + "itemname": "Gage block holder", + "latestrevision": null, + "lowstockthreshold": 5, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "printeditemid": 1, + "printnotes": null, + "quantityonhand": 0 + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/printedparts/items", + "auth": "permission:printedparts.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a printed item.", + "example": "curl -X POST http://localhost:5001/api/printedparts/items -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "gagelabtag": "wjrp0117", + "itemname": "Gage block holder" + }, + "responseexample": { + "data": { + "binlocation": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "gagelabtag": "WJRP0117", + "imageurl": null, + "isactive": true, + "islowstock": true, + "itemcode": "3DP0001", + "itemdescription": null, + "itemname": "Gage block holder", + "latestrevision": null, + "lowstockthreshold": 5, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "printeditemid": 1, + "printnotes": null, + "quantityonhand": 0 + }, + "message": "Printed item created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/printedparts/items/", + "auth": "permission:printedparts.delete", + "params": "path: item_id (int)", + "purpose": "Soft-retire an item; its ledger history stays.", + "example": "curl -X DELETE http://localhost:5001/api/printedparts/items/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "message": "Printed item retired", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/printedparts/items/", + "auth": "permission:printedparts.view", + "params": "path: item_id (int)", + "purpose": "Get one printed item with its recent transactions.", + "example": "curl http://localhost:5001/api/printedparts/items/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/printedparts/items/", + "auth": "permission:printedparts.edit", + "params": "path: item_id (int); body: JSON (fields not yet documented)", + "purpose": "Update catalog fields. Quantity moves ONLY through the ledger.", + "example": "curl -X PUT http://localhost:5001/api/printedparts/items/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "POST", + "path": "/api/printedparts/items//adjust", + "auth": "permission:printedparts.restock", + "params": "path: item_id (int); body: JSON (fields not yet documented)", + "purpose": "Correct the count (damage, recount). Body: {quantitychange, reason, badge}.", + "example": "curl -X POST http://localhost:5001/api/printedparts/items/1/adjust -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/printedparts/items//files", + "auth": "permission:printedparts.view", + "params": "path: item_id (int)", + "purpose": "Revision history, newest first.", + "example": "curl http://localhost:5001/api/printedparts/items/1/files -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [ + { + "fileid": 2, + "filename": "clip_v2.gcode", + "filesize": 8, + "printeditemid": 1, + "revision": 2, + "uploadedby": "testadmin", + "uploadeddate": "2026-01-01T00:00:00.000000Z", + "uploadnote": null + }, + { + "fileid": 1, + "filename": "clip_v1.stl", + "filesize": 10, + "printeditemid": 1, + "revision": 1, + "uploadedby": "testadmin", + "uploadeddate": "2026-01-01T00:00:00.000000Z", + "uploadnote": "initial" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/printedparts/items//files", + "auth": "permission:printedparts.edit", + "params": "path: item_id (int); body: JSON (fields not yet documented)", + "purpose": "Upload the next revision of the item's print file.", + "example": "curl -X POST http://localhost:5001/api/printedparts/items/1/files -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "fileid": 1, + "filename": "clip_v1.stl", + "filesize": 10, + "printeditemid": 1, + "revision": 1, + "uploadedby": "testadmin", + "uploadeddate": "2026-01-01T00:00:00.000000Z", + "uploadnote": "initial" + }, + "message": "Revision 1 uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/printedparts/items//image", + "auth": "permission:printedparts.delete", + "params": "path: item_id (int)", + "purpose": "Clear an item image; delete the file only if this plugin owns it.", + "example": "curl -X DELETE http://localhost:5001/api/printedparts/items/1/image -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/printedparts/items//image", + "auth": "permission:printedparts.edit", + "params": "path: item_id (int); body: JSON (fields not yet documented)", + "purpose": "Upload (or replace) the photo for an item (multipart file=).", + "example": "curl -X POST http://localhost:5001/api/printedparts/items/1/image -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "POST", + "path": "/api/printedparts/items//restock", + "auth": "permission:printedparts.restock", + "params": "path: item_id (int); body: JSON (fields not yet documented)", + "purpose": "Add freshly printed stock. Body: {quantity, badge}.", + "example": "curl -X POST http://localhost:5001/api/printedparts/items/1/restock -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "badge": "", + "quantity": 10 + }, + "responseexample": { + "data": { + "binlocation": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "gagelabtag": null, + "imageurl": null, + "isactive": true, + "islowstock": false, + "itemcode": "3DP-9001", + "itemdescription": null, + "itemname": "Test clip", + "latestrevision": null, + "lowstockthreshold": 5, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "printeditemid": 1, + "printnotes": null, + "quantityonhand": 10 + }, + "message": "Stock added", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/printedparts/items//restore", + "auth": "permission:printedparts.delete", + "params": "path: item_id (int); body: JSON (fields not yet documented)", + "purpose": "Bring a retired item back; code, photo, and history are intact.", + "example": "curl -X POST http://localhost:5001/api/printedparts/items/1/restore -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "binlocation": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "gagelabtag": null, + "imageurl": null, + "isactive": true, + "islowstock": true, + "itemcode": "3DP-9001", + "itemdescription": null, + "itemname": "Test clip", + "latestrevision": null, + "lowstockthreshold": 5, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "printeditemid": 1, + "printnotes": null, + "quantityonhand": 0 + }, + "message": "Printed item restored", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/printedparts/kiosk/item/", + "auth": "api-key-optional (kiosk: the per-device token in the X-Kiosk-Token header, OR a caller address inside the site's kiosk CIDR allowlist - an allowlisted caller sends no credential; 404 while the site's kiosk flag is off)", + "params": "path: itemcode (string)", + "purpose": "Item summary for a scanned bin barcode. Kiosk-token gated.", + "example": "curl http://localhost:5001/api/printedparts/kiosk/item/x", + "responseexample": { + "data": { + "binlocation": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "gagelabtag": "WJRP0117", + "imageurl": null, + "isactive": true, + "islowstock": true, + "itemcode": "3DP0001", + "itemdescription": null, + "itemname": "Gage block holder", + "latestrevision": null, + "lowstockthreshold": 5, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "printeditemid": 1, + "printnotes": null, + "quantityonhand": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/printedparts/kiosk/provisioning-link", + "auth": "permission:settings.edit", + "params": "not documented", + "purpose": "The URL that provisions a screen, token included, for an administrator.", + "example": "curl http://localhost:5001/api/printedparts/kiosk/provisioning-link -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "POST", + "path": "/api/printedparts/kiosk/take", + "auth": "api-key-optional (kiosk: the per-device token in the X-Kiosk-Token header, OR a caller address inside the site's kiosk CIDR allowlist - an allowlisted caller sends no credential; 404 while the site's kiosk flag is off)", + "params": "body: JSON (fields not yet documented)", + "purpose": "Take parts from a bin. Body: {itemcode, badge, quantity}.", + "example": "curl -X POST http://localhost:5001/api/printedparts/kiosk/take -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "badge": "", + "itemcode": "WJRP7000|3", + "quantity": 1 + }, + "responseexample": { + "data": { + "binlocation": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "gagelabtag": "WJRP7000", + "imageurl": null, + "isactive": true, + "islowstock": false, + "itemcode": "3DP-7000", + "itemdescription": null, + "itemname": "Rev part", + "latestrevision": null, + "lowstockthreshold": 2, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "printeditemid": 1, + "printnotes": null, + "quantityonhand": 8 + }, + "message": "Took 1, 8 left", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/printedparts/reports/by-person", + "auth": "permission:printedparts.view", + "params": "not documented", + "purpose": "Takes grouped by employee, optionally bounded by ?days= (default 30).", + "example": "curl http://localhost:5001/api/printedparts/reports/by-person -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "days", + "type": "integer", + "required": false, + "default": 30 + }, + { + "name": "format", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": { + "columns": [ + "employeesso", + "employeename", + "takes", + "" + ], + "days": 30, + "rows": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/printedparts/reports/consumption", + "auth": "permission:printedparts.view", + "params": "not documented", + "purpose": "Takes per item, optionally bounded by ?days= (default 30).", + "example": "curl http://localhost:5001/api/printedparts/reports/consumption -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "days", + "type": "integer", + "required": false, + "default": 30 + }, + { + "name": "format", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": { + "columns": [ + "itemcode", + "itemname", + "takes", + "" + ], + "days": 30, + "rows": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/printedparts/reports/stock", + "auth": "permission:printedparts.view", + "params": "not documented", + "purpose": "Stock levels with low-stock flags and the cache-vs-ledger reconcile.", + "example": "curl http://localhost:5001/api/printedparts/reports/stock -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "format", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": { + "columns": [ + "itemcode", + "itemname", + "binlocation", + "" + ], + "rows": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + } + ] + }, + { + "surface": "plugin-printers", + "endpoints": [ + { + "method": "GET", + "path": "/api/printers", + "auth": "permission:printers.view", + "params": "not documented", + "purpose": "List all printers with filtering and pagination.", + "example": "curl http://localhost:5001/api/printers -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "assetnumber", + "type": "string", + "required": false + }, + { + "name": "businessunit_id", + "type": "integer", + "required": false + }, + { + "name": "businessunitid", + "type": "integer", + "required": false + }, + { + "name": "dir", + "type": "string", + "required": false, + "default": "asc" + }, + { + "name": "location_id", + "type": "integer", + "required": false + }, + { + "name": "locationid", + "type": "integer", + "required": false + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "sort", + "type": "string", + "required": false, + "default": "hostname" + }, + { + "name": "type_id", + "type": "integer", + "required": false + }, + { + "name": "typeid", + "type": "integer", + "required": false + }, + { + "name": "vendor_id", + "type": "integer", + "required": false + }, + { + "name": "vendorid", + "type": "integer", + "required": false + } + ], + "responseexample": { + "data": [ + { + "assetid": 1, + "assetnumber": "PR-1", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "printer", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "ipaddress": "", + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "printer": { + "assetid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "hostname": "", + "installpath": null, + "isactive": true, + "iscolor": false, + "iscsf": false, + "isduplex": false, + "isnetwork": true, + "modelname": "ZT411", + "modelnumberid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "pin": null, + "printerid": 1, + "printertypeid": 1, + "printertypename": "Thermal", + "sharename": null, + "vendorid": null, + "windowsname": null + }, + "serialnumber": "", + "statusid": 1 + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/printers", + "auth": "permission:printers.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create new printer (creates both Asset and Printer records).", + "example": "curl -X POST http://localhost:5001/api/printers -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "assetnumber": "CSF01-Materials-HP", + "hostname": "", + "installpath": "", + "mapx": 120, + "mapy": 240 + }, + "responseexample": { + "data": { + "assetid": 1, + "assetnumber": "SpoolsInspection-Xerox", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "printer", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "printer": { + "assetid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "hostname": "", + "installpath": null, + "isactive": true, + "iscolor": false, + "iscsf": false, + "isduplex": false, + "isnetwork": true, + "modelname": "VersaLink C7125", + "modelnumberid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "pin": null, + "printerid": 1, + "printertypeid": null, + "sharename": null, + "vendorfrommodel": true, + "vendorid": null, + "vendorname": "Xerox", + "windowsname": null + }, + "serialnumber": "", + "statusid": 1 + }, + "message": "Printer created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/printers/", + "auth": "permission:printers.delete", + "params": "path: printer_id (int)", + "purpose": "Delete (soft delete) printer.", + "example": "curl -X DELETE http://localhost:5001/api/printers/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/printers/", + "auth": "permission:printers.view", + "params": "path: printer_id (int)", + "purpose": "Get a single printer with full details.", + "example": "curl http://localhost:5001/api/printers/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "assetid": 1, + "assetnumber": "PR-2001", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "printer", + "businessunitid": null, + "communications": [], + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "drivers": [], + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "printer": { + "assetid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "hostname": "", + "imageurl": "/api/models/images/model-1.png", + "installpath": null, + "isactive": true, + "iscolor": false, + "iscsf": false, + "isduplex": false, + "isnetwork": true, + "modelname": "IMG-MODEL-1", + "modelnumberid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "pin": null, + "printerid": 1, + "printertypeid": null, + "sharename": null, + "vendorid": null, + "windowsname": null + }, + "serialnumber": "", + "statusid": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/printers/", + "auth": "permission:printers.edit", + "params": "path: printer_id (int); body: JSON (fields not yet documented)", + "purpose": "Update printer (both Asset and Printer records).", + "example": "curl -X PUT http://localhost:5001/api/printers/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "gaugelabreference": "GL-2", + "maintenancereference": "MNT-2" + }, + "responseexample": { + "data": { + "assetid": 1, + "assetnumber": "AST-printer", + "assettypecolor": null, + "assettypeid": 2, + "assettypename": "printer", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": "GL-2", + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": "MNT-2", + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "printer": { + "assetid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "hostname": "", + "installpath": null, + "isactive": true, + "iscolor": false, + "iscsf": false, + "isduplex": false, + "isnetwork": true, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "pin": null, + "printerid": 1, + "printertypeid": null, + "sharename": null, + "vendorid": null, + "windowsname": null + }, + "serialnumber": "", + "statusid": 1 + }, + "message": "Printer updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/printers//supplies", + "auth": "permission:printers.view", + "params": "path: printer_id (int)", + "purpose": "Get supply levels from Zabbix (real-time lookup).", + "example": "curl http://localhost:5001/api/printers/1/supplies -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/printers/assignments/for-asset/", + "auth": "permission:printers.view", + "params": "path: asset_id (int)", + "purpose": "What THIS asset is assigned, without inheritance.", + "example": "curl http://localhost:5001/api/printers/assignments/for-asset/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "assetid": 4, + "defaultprinterassetid": null, + "printerassetids": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/printers/assignments/for-asset/", + "auth": "permission:printers.edit", + "params": "path: asset_id (int); body: JSON (fields not yet documented)", + "purpose": "Reconcile one asset's whole printer assignment in a single call.", + "example": "curl -X PUT http://localhost:5001/api/printers/assignments/for-asset/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "defaultprinterassetid": 4, + "printerassetids": [ + 4, + 5 + ] + }, + "responseexample": { + "data": { + "assetid": 3, + "defaultprinterassetid": 4, + "printerassetids": [ + 4, + 5 + ] + }, + "message": "Printer assignment updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/printers/assignments/seed-from-observed/", + "auth": "permission:printers.edit", + "params": "path: asset_id (int); body: JSON (fields not yet documented)", + "purpose": "Write what a PC observed as the assignment of the asset in the path.", + "example": "curl -X POST http://localhost:5001/api/printers/assignments/seed-from-observed/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/printers/by-asset/", + "auth": "permission:printers.view", + "params": "path: asset_id (int)", + "purpose": "Get printer data by asset ID.", + "example": "curl http://localhost:5001/api/printers/by-asset/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/printers/dashboard/summary", + "auth": "permission:printers.view", + "params": "not documented", + "purpose": "Get printer dashboard summary data.", + "example": "curl http://localhost:5001/api/printers/dashboard/summary -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "bytype": [], + "byvendor": [], + "criticalsupplies": 0, + "lowsupplies": 0, + "online": 0, + "total": 0, + "totalprinters": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/printers/dashboard/supplies", + "auth": "permission:printers.view", + "params": "not documented", + "purpose": "Printers needing a cartridge, flattened to one row per printer.", + "example": "curl http://localhost:5001/api/printers/dashboard/supplies -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "rows": [ + { + "iscritical": true, + "levelid": null, + "location": "Shop floor", + "mapx": null, + "mapy": null, + "printerid": 2, + "printername": "Bravo", + "supplies": [ + { + "level": "critical", + "text": "Black Toner 0%", + "title": "No part number on file for this model" + } + ], + "worstremaining": 0 + }, + { + "iscritical": true, + "levelid": null, + "location": "Shop floor", + "mapx": null, + "mapy": null, + "printerid": 3, + "printername": "Charlie", + "supplies": [ + { + "level": "critical", + "text": "Black Toner 2%", + "title": "No part number on file for this model" + } + ], + "worstremaining": 2 + }, + { + "iscritical": true, + "levelid": null, + "location": "Shop floor", + "mapx": null, + "mapy": null, + "printerid": 1, + "printername": "Alpha", + "supplies": [ + { + "level": "critical", + "text": "Black Toner 4%", + "title": "No part number on file for this model" + } + ], + "worstremaining": 4 + }, + "" + ], + "unavailable": null + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/printers/drivers", + "auth": "permission:printers.view", + "params": "not documented", + "purpose": "List printer drivers. ?active=false includes inactive ones.", + "example": "curl http://localhost:5001/api/printers/drivers -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + } + ], + "responseexample": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/printers/drivers", + "auth": "permission:printers.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: printers_asset.create_driver has no docstring.", + "example": "curl -X POST http://localhost:5001/api/printers/drivers -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "drivername": "HP Universal Printing PCL 6", + "location": "", + "name": "HP universal", + "vendorid": 1 + }, + "responseexample": { + "data": { + "description": null, + "driverid": 1, + "drivername": "HP Universal Printing PCL 6", + "isactive": true, + "location": "", + "modelname": null, + "modelnumberid": null, + "name": "HP universal", + "vendorid": 1 + }, + "message": "Driver created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/printers/drivers/", + "auth": "permission:printers.delete", + "params": "path: driver_id (int)", + "purpose": "UNDOCUMENTED: printers_asset.delete_driver has no docstring.", + "example": "curl -X DELETE http://localhost:5001/api/printers/drivers/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/printers/drivers/", + "auth": "permission:printers.edit", + "params": "path: driver_id (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: printers_asset.update_driver has no docstring.", + "example": "curl -X PUT http://localhost:5001/api/printers/drivers/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "drivername": "Xerox Global Print Driver PCL6", + "vendorid": 2 + }, + "responseexample": { + "data": { + "description": null, + "driverid": 1, + "drivername": "Xerox Global Print Driver PCL6", + "isactive": true, + "location": "", + "modelname": null, + "modelnumberid": null, + "name": "Xerox universal", + "vendorid": 2 + }, + "message": "Driver updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/printers/for-host/", + "auth": "jwt-optional (works signed out; the handler decides what shows)", + "params": "path: hostname (string)", + "purpose": "Printers assigned to a PC, by hostname, with what it takes to install one.", + "example": "curl http://localhost:5001/api/printers/for-host/x -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "assetid": 1, + "assetnumber": "1001", + "defaultprinterid": null, + "hostname": "", + "printers": [ + { + "assetid": 4, + "driverid": null, + "driverlocation": null, + "drivername": null, + "hostname": "", + "inheritedfromassetid": 3, + "installpath": null, + "ipaddress": "", + "isdefault": false, + "port": null, + "printerid": 1, + "queuename": "PRINTER-A", + "sharename": null, + "windowsname": "PRINTER-A" + }, + { + "assetid": 5, + "driverid": null, + "driverlocation": null, + "drivername": null, + "hostname": "", + "inheritedfromassetid": 3, + "installpath": null, + "ipaddress": "", + "isdefault": false, + "port": null, + "printerid": 2, + "queuename": "PRINTER-B", + "sharename": null, + "windowsname": "PRINTER-B" + }, + { + "assetid": 6, + "driverid": null, + "driverlocation": null, + "drivername": null, + "hostname": "", + "inheritedfromassetid": 3, + "installpath": null, + "ipaddress": "", + "isdefault": false, + "port": null, + "printerid": 3, + "queuename": "PRINTER-C", + "sharename": null, + "windowsname": "PRINTER-C" + } + ], + "source": "inherited" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/printers/install-batch", + "auth": "jwt-optional (works signed out; the handler decides what shows)", + "params": "not documented", + "purpose": "Generate a Windows .bat that installs the selected printers.", + "example": "curl http://localhost:5001/api/printers/install-batch -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "printerids", + "type": "string", + "required": false + } ] - } -] + }, + { + "method": "GET", + "path": "/api/printers/install-list", + "auth": "jwt-optional (works signed out; the handler decides what shows)", + "params": "not documented", + "purpose": "Flat, unpaginated list of network printers for the printer installer.", + "example": "curl http://localhost:5001/api/printers/install-list -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "format", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [ + { + "hostname": "", + "installpath": "", + "ipaddress": "", + "iscsf": false, + "levelid": null, + "locationname": null, + "machinenumber": "CSF01-Materials-HP", + "mapx": 120, + "mapy": 240, + "modelnumber": null, + "name": "CSF01-Materials-HP", + "printerid": 1, + "sharename": null, + "vendorname": "", + "windowsname": null + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/printers/lookup", + "auth": "permission:printers.view", + "params": "not documented", + "purpose": "Find a printer by IP or FQDN. Parity with the classic printerlookup.asp.", + "example": "curl http://localhost:5001/api/printers/lookup -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "fqdn", + "type": "string", + "required": false + }, + { + "name": "ip", + "type": "string", + "required": false + } + ] + }, + { + "method": "GET", + "path": "/api/printers/lowsupplies", + "auth": "permission:printers.view", + "params": "not documented", + "purpose": "Get printers with low or critical supply levels.", + "example": "curl http://localhost:5001/api/printers/lowsupplies -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "printers": [], + "summary": { + "critical": 0, + "low": 0, + "total_checked": 0 + } + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/printers/models", + "auth": "permission:printers.view", + "params": "not documented", + "purpose": "List models with a supply count, for the supply-management picker.", + "example": "curl http://localhost:5001/api/printers/models -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + }, + { + "name": "vendor_id", + "type": "integer", + "required": false + }, + { + "name": "vendorid", + "type": "integer", + "required": false + }, + { + "name": "withsupplies", + "type": "string", + "required": false, + "default": "" + } + ], + "responseexample": { + "data": [ + { + "modelnumber": "TestModel C999", + "modelnumberid": 1, + "supplycount": 1, + "vendor": "TestVendor", + "vendorid": 1 + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/printers/models//supplies", + "auth": "permission:printers.view", + "params": "path: modelnumberid (int)", + "purpose": "List all supplies mapped to a model.", + "example": "curl http://localhost:5001/api/printers/models/1/supplies -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "modelnumber": "TestModel C999", + "modelnumberid": 1, + "supplies": [ + { + "capacitytier": "standard", + "color": "black", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "marketingname": "414A Black", + "modelnumber": "TestModel C999", + "modelnumberid": 1, + "modelsupplyid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "pageyield": 2400, + "partnumber": "W2020A", + "supplytype": "toner" + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/printers/models//supplies", + "auth": "permission:printers.create", + "params": "path: modelnumberid (int); body: JSON (fields not yet documented)", + "purpose": "Add a supply to a model.", + "example": "curl -X POST http://localhost:5001/api/printers/models/1/supplies -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "capacitytier": "standard", + "color": "black", + "marketingname": "414A Black", + "pageyield": 2400, + "partnumber": "W2020A", + "supplytype": "toner" + }, + "responseexample": { + "data": { + "capacitytier": "standard", + "color": "black", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "marketingname": "414A Black", + "modelnumber": "TestModel C999", + "modelnumberid": 1, + "modelsupplyid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "pageyield": 2400, + "partnumber": "W2020A", + "supplytype": "toner" + }, + "message": "Supply added", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/printers/observed/", + "auth": "permission:printers.view", + "params": "path: hostname (string)", + "purpose": "What a host last reported, each queue judged against what it is assigned.", + "example": "curl http://localhost:5001/api/printers/observed/x -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/printers/observed/for-asset/", + "auth": "permission:printers.view", + "params": "path: asset_id (int)", + "purpose": "The same comparison, reached from an asset page: one block per host.", + "example": "curl http://localhost:5001/api/printers/observed/for-asset/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "assetid": 1, + "assetnumber": "1001", + "hosts": [ + { + "assetid": 1, + "assetnumber": "1001", + "hostname": "", + "observedat": "2026-01-01T00:00:00.000000", + "queues": [ + { + "classification": "extra", + "driftfields": [], + "drivername": "HP Universal Printing PS", + "expecteddrivername": null, + "expectedportaddress": null, + "expectedqueuename": null, + "inheritedfromassetid": null, + "isassigneddefault": false, + "isdefault": true, + "isshared": false, + "portaddress": "", + "portname": "IP_10.20.0.11", + "printerassetid": 5, + "printerid": 1, + "printername": "Bay label printer", + "queuename": "PRINTER-A" + }, + { + "classification": "extra", + "driftfields": [], + "drivername": "HP Universal Printing PS", + "expecteddrivername": null, + "expectedportaddress": null, + "expectedqueuename": null, + "inheritedfromassetid": null, + "isassigneddefault": false, + "isdefault": false, + "isshared": false, + "portaddress": "", + "portname": "IP_10.20.0.12", + "printerassetid": 6, + "printerid": 2, + "printername": "Bay laser printer", + "queuename": "PRINTER-B" + }, + { + "classification": "unknown", + "driftfields": [], + "drivername": "HP Universal Printing PS", + "expecteddrivername": null, + "expectedportaddress": null, + "expectedqueuename": null, + "inheritedfromassetid": null, + "isassigneddefault": false, + "isdefault": false, + "isshared": false, + "portaddress": "", + "portname": "IP_10.20.0.99", + "printerassetid": null, + "printerid": null, + "printername": null, + "queuename": "Reception Copier" + } + ], + "seedcandidate": { + "defaultprinterassetid": 5, + "printerassetids": [ + 5, + 6 + ], + "skipped": [ + { + "drivername": "HP Universal Printing PS", + "isdefault": false, + "portaddress": "", + "portname": "IP_10.20.0.99", + "queuename": "Reception Copier" + } + ] + }, + "source": "none", + "summary": { + "drifted": 0, + "extra": 2, + "matching": 0, + "missing": 0, + "unknown": 1 + } + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/printers/pc-default", + "auth": "jwt-optional (works signed out; the handler decides what shows)", + "params": "not documented", + "purpose": "Default printer for a PC, by machine (asset) number.", + "example": "curl http://localhost:5001/api/printers/pc-default -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "format", + "type": "string", + "required": false + }, + { + "name": "machine", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": { + "printerid": 1, + "windowsname": "HP-CSF04-Materials" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "DELETE", + "path": "/api/printers/supplies/", + "auth": "permission:printers.delete", + "params": "path: modelsupplyid (int)", + "purpose": "Delete a model supply.", + "example": "curl -X DELETE http://localhost:5001/api/printers/supplies/1 -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "message": "Supply deleted", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PUT", + "path": "/api/printers/supplies/", + "auth": "permission:printers.edit", + "params": "path: modelsupplyid (int); body: JSON (fields not yet documented)", + "purpose": "Update a model supply.", + "example": "curl -X PUT http://localhost:5001/api/printers/supplies/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "capacitytier": "high", + "marketingname": "414X Black" + }, + "responseexample": { + "data": { + "capacitytier": "high", + "color": "black", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "marketingname": "414X Black", + "modelnumber": "TestModel C999", + "modelnumberid": 1, + "modelsupplyid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "pageyield": null, + "partnumber": "W2020A", + "supplytype": "toner" + }, + "message": "Supply updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/printers/supplies/forecast", + "auth": "permission:printers.view", + "params": "not documented", + "purpose": "Days-to-empty and replacement counts per printer.", + "example": "curl http://localhost:5001/api/printers/supplies/forecast -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "days", + "type": "integer", + "required": false, + "default": 90 + } + ], + "responseexample": { + "data": { + "available": false, + "cartridges": [], + "days": 90, + "horizondays": 14, + "orderlist": [], + "reason": "Zabbix is not configured or not reachable", + "unestimated": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/printers/supplies/meta", + "auth": "permission:printers.view", + "params": "not documented", + "purpose": "Allowed values for supply type, color, and capacity tier (for the UI).", + "example": "curl http://localhost:5001/api/printers/supplies/meta -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "capacitytiers": [ + "standard", + "high", + "extrahigh", + "" + ], + "colors": [ + "black", + "cyan", + "magenta", + "" + ], + "supplytypes": [ + "toner", + "drum", + "waste", + "" + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/printers/supplies/refresh", + "auth": "permission:printers.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Clear cached Zabbix supply data so the next read pulls fresh values.", + "example": "curl -X POST http://localhost:5001/api/printers/supplies/refresh -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": {}, + "responseexample": { + "message": "Supply cache cleared", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/printers/types", + "auth": "permission:printers.view", + "params": "not documented", + "purpose": "List all printer types.", + "example": "curl http://localhost:5001/api/printers/types -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/printers/types", + "auth": "permission:printers.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Create a new printer type.", + "example": "curl -X POST http://localhost:5001/api/printers/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/printers/types/", + "auth": "permission:printers.delete", + "params": "path: type_id (int)", + "purpose": "Delete a printer type. Refused if any printer still uses it.", + "example": "curl -X DELETE http://localhost:5001/api/printers/types/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/printers/types/", + "auth": "permission:printers.view", + "params": "path: type_id (int)", + "purpose": "Get a single printer type.", + "example": "curl http://localhost:5001/api/printers/types/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/printers/types/", + "auth": "permission:printers.edit", + "params": "path: type_id (int); body: JSON (fields not yet documented)", + "purpose": "Update a printer type.", + "example": "curl -X PUT http://localhost:5001/api/printers/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + } + ] + }, + { + "surface": "plugin-slides", + "endpoints": [ + { + "method": "GET", + "path": "/api/slides/", + "auth": "permission:slides.view or slides.manage", + "params": "path: surface (string)", + "purpose": "UNDOCUMENTED: slides.list_slides has no docstring.", + "example": "curl http://localhost:5001/api/slides/x -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "PATCH", + "path": "/api/slides//", + "auth": "permission:slides.manage", + "params": "path: surface (string), slideid (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: slides.update_slide has no docstring.", + "example": "curl -X PATCH http://localhost:5001/api/slides/x/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "POST", + "path": "/api/slides//delete", + "auth": "permission:slides.manage", + "params": "path: surface (string); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: slides.delete_slides has no docstring.", + "example": "curl -X POST http://localhost:5001/api/slides/x/delete -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "POST", + "path": "/api/slides//order", + "auth": "permission:slides.manage", + "params": "path: surface (string); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: slides.reorder_slides has no docstring.", + "example": "curl -X POST http://localhost:5001/api/slides/x/order -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "POST", + "path": "/api/slides//upload", + "auth": "permission:slides.manage", + "params": "path: surface (string); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: slides.upload_slides has no docstring.", + "example": "curl -X POST http://localhost:5001/api/slides/x/upload -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/slides/feed", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "not documented", + "purpose": "Flat playlist for a surface. NOT wrapped in the success_response envelope so the screensaver's existing parser needs no change.", + "example": "curl http://localhost:5001/api/slides/feed", + "queryparams": [ + { + "name": "surface", + "type": "string", + "required": false, + "default": "lobby" + } + ], + "responseexample": { + "basepath": "/api/slides/img/lobby/", + "interval": 10, + "slides": [], + "success": true, + "surface": "lobby" + } + }, + { + "method": "GET", + "path": "/api/slides/img//", + "auth": "none (reviewed: named in the public-read or mutation sweep)", + "params": "path: surface (string), filename (path)", + "purpose": "Serve a slide image with a path-traversal guard.", + "example": "curl http://localhost:5001/api/slides/img/x/example.png" + } + ] + }, + { + "surface": "plugin-tools", + "endpoints": [ + { + "method": "POST", + "path": "/api/tools/secrets", + "auth": "jwt-optional (works signed out; the handler decides what shows)", + "params": "body: JSON (fields not yet documented)", + "purpose": "Store an already-encrypted secret and return its token.", + "example": "curl -X POST http://localhost:5001/api/tools/secrets -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "ciphertext": "", + "iv": "MTIzNDU2Nzg5MDEy", + "views": 100 + }, + "responseexample": { + "data": { + "createddate": "2026-01-01T00:00:00.000000", + "expiresat": "2026-01-01T00:00:00.000000", + "token": "", + "viewcount": 0, + "viewsremaining": 100 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/tools/secrets/", + "auth": "none (the unguessable token in the path is the authorisation)", + "params": "path: token (string)", + "purpose": "Whether a link is still live, WITHOUT spending it.", + "example": "curl http://localhost:5001/api/tools/secrets/x", + "responseexample": { + "data": { + "available": false + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/tools/secrets/", + "auth": "none (the unguessable token in the path is the authorisation; redeeming spends the secret)", + "params": "path: token (string); body: JSON (fields not yet documented)", + "purpose": "Hand over the ciphertext once, then spend the view.", + "example": "curl -X POST http://localhost:5001/api/tools/secrets/x -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "ciphertext": "", + "iv": "MTIzNDU2Nzg5MDEy" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + } + ] + }, + { + "surface": "plugin-usb", + "endpoints": [ + { + "method": "GET", + "path": "/api/usb", + "auth": "permission:usb.view", + "params": "not documented", + "purpose": "List USB devices with checkout status.", + "example": "curl http://localhost:5001/api/usb -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + }, + { + "name": "search", + "type": "string", + "required": false, + "default": "" + }, + { + "name": "status", + "type": "string", + "required": false, + "default": "" + } + ] + }, + { + "method": "POST", + "path": "/api/usb", + "auth": "permission:usb.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "Add a device (starts checked-in).", + "example": "curl -X POST http://localhost:5001/api/usb -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/usb/", + "auth": "permission:usb.view", + "params": "path: device_id (string)", + "purpose": "Get a single device plus its last 20 check-in/out log rows.", + "example": "curl http://localhost:5001/api/usb/x -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/usb/", + "auth": "permission:usb.edit", + "params": "path: device_id (string); body: JSON (fields not yet documented)", + "purpose": "Edit device_desc / device_owner / locker_location / status.", + "example": "curl -X PUT http://localhost:5001/api/usb/x -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "POST", + "path": "/api/usb//checkin", + "auth": "permission:usb.create", + "params": "path: device_id (string); body: JSON (fields not yet documented)", + "purpose": "Check a device back in from a badge.", + "example": "curl -X POST http://localhost:5001/api/usb/x/checkin -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "POST", + "path": "/api/usb//checkout", + "auth": "permission:usb.create", + "params": "path: device_id (string); body: JSON (fields not yet documented)", + "purpose": "Check a device out to a badge.", + "example": "curl -X POST http://localhost:5001/api/usb/x/checkout -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "readsbody": true + }, + { + "method": "GET", + "path": "/api/usb//history", + "auth": "permission:usb.view", + "params": "path: device_id (string)", + "purpose": "Paginated check-in/out log for one device.", + "example": "curl http://localhost:5001/api/usb/x/history -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + } + ] + }, + { + "method": "POST", + "path": "/api/usb//retire", + "auth": "permission:usb.edit", + "params": "path: device_id (string); body: JSON (fields not yet documented)", + "purpose": "Retire a device (status -> retired).", + "example": "curl -X POST http://localhost:5001/api/usb/x/retire -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + }, + { + "method": "GET", + "path": "/api/usb/checkouts", + "auth": "permission:usb.view", + "params": "not documented", + "purpose": "List check-out log rows.", + "example": "curl http://localhost:5001/api/usb/checkouts -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "" + }, + { + "name": "badge", + "type": "string", + "required": false, + "default": "" + }, + { + "name": "page", + "type": "integer", + "required": false, + "default": 1 + }, + { + "name": "per_page", + "type": "integer", + "required": false + }, + { + "name": "perpage", + "type": "integer", + "required": false + } + ] + }, + { + "method": "GET", + "path": "/api/usb/checkouts/active", + "auth": "permission:usb.view", + "params": "not documented", + "purpose": "List the latest check-out log row for every currently checked-out device.", + "example": "curl http://localhost:5001/api/usb/checkouts/active -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "badge", + "type": "string", + "required": false, + "default": "" + } + ] + }, + { + "method": "POST", + "path": "/api/usb/kiosk/checkin", + "auth": "api-key-optional (kiosk: the per-device token in the X-Kiosk-Token header, OR a caller address inside the site's kiosk CIDR allowlist - an allowlisted caller sends no credential; 404 while the site's kiosk flag is off)", + "params": "body: JSON (fields not yet documented)", + "purpose": "Check a device in at the kiosk. Body: {device_id, badge, forsomeoneelse}.", + "example": "curl -X POST http://localhost:5001/api/usb/kiosk/checkin -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "badge": "", + "device_id": "USB-1", + "forsomeoneelse": true + }, + "responseexample": { + "data": { + "device_id": "USB-1" + }, + "message": "USB-1 checked in", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/usb/kiosk/checkout", + "auth": "api-key-optional (kiosk: the per-device token in the X-Kiosk-Token header, OR a caller address inside the site's kiosk CIDR allowlist - an allowlisted caller sends no credential; 404 while the site's kiosk flag is off)", + "params": "body: JSON (fields not yet documented)", + "purpose": "Check a device out at the kiosk. Body: {device_id, badge}.", + "example": "curl -X POST http://localhost:5001/api/usb/kiosk/checkout -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "badge": "", + "device_id": "USB-1" + }, + "responseexample": { + "data": { + "device_id": "USB-1", + "holdername": "Dana Reyes" + }, + "message": "USB-1 checked out to Dana Reyes", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/usb/kiosk/device/", + "auth": "api-key-optional (kiosk: the per-device token in the X-Kiosk-Token header, OR a caller address inside the site's kiosk CIDR allowlist - an allowlisted caller sends no credential; 404 while the site's kiosk flag is off)", + "params": "path: device_id (string)", + "purpose": "What the kiosk shows after a device is scanned.", + "example": "curl http://localhost:5001/api/usb/kiosk/device/x", + "responseexample": { + "data": { + "device_desc": "Blue Kingston", + "device_id": "USB-1", + "holdername": "Dana Reyes", + "locker_location": null, + "status": "checked-out" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/usb/kiosk/others", + "auth": "api-key-optional (kiosk: the per-device token in the X-Kiosk-Token header, OR a caller address inside the site's kiosk CIDR allowlist - an allowlisted caller sends no credential; 404 while the site's kiosk flag is off)", + "params": "body: JSON (fields not yet documented)", + "purpose": "Drives checked out to OTHER people, with holder names. Body: {badge}.", + "example": "curl -X POST http://localhost:5001/api/usb/kiosk/others -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "badge": "" + }, + "responseexample": { + "data": [ + { + "device_desc": "Blue Kingston", + "device_id": "USB-1", + "holdername": "Dana Reyes", + "locker_location": null + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/usb/kiosk/provisioning-link", + "auth": "permission:settings.edit", + "params": "not documented", + "purpose": "The URL that provisions a screen, token included, for an administrator.", + "example": "curl http://localhost:5001/api/usb/kiosk/provisioning-link -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "expiresinminutes": 30, + "url": "/api/usb/kiosk/setup?code=" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/usb/kiosk/session", + "auth": "api-key-optional (kiosk: the per-device token in the X-Kiosk-Token header, OR a caller address inside the site's kiosk CIDR allowlist - an allowlisted caller sends no credential; 404 while the site's kiosk flag is off)", + "params": "body: JSON (fields not yet documented)", + "purpose": "What one badge may do right now. Body: {badge}.", + "example": "curl -X POST http://localhost:5001/api/usb/kiosk/session -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "badge": "" + }, + "responseexample": { + "data": { + "available": [ + { + "device_desc": "Second", + "device_id": "USB-2", + "locker_location": null + } + ], + "name": "Dana Reyes", + "otherscount": 0, + "yours": [ + { + "device_desc": "Blue Kingston", + "device_id": "USB-1", + "locker_location": null + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "GET", + "path": "/api/usb/kiosk/setup", + "auth": "setup-code (single-use kiosk setup code in ?code=, issued by the permission-gated provisioning link; spent on any attempt, 410 once used or expired)", + "params": "not documented", + "purpose": "Trade a setup code for this browser's kiosk cookie, then open the kiosk.", + "example": "curl http://localhost:5001/api/usb/kiosk/setup", + "queryparams": [ + { + "name": "code", + "type": "string", + "required": false + } + ] + }, + { + "method": "GET", + "path": "/api/usb/reports/audit", + "auth": "permission:usb.view", + "params": "not documented", + "purpose": "Every check-out and check-in in the window, newest first.", + "example": "curl http://localhost:5001/api/usb/reports/audit -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "columns": [ + "timestamp", + "action", + "device_id", + "" + ], + "days": 365, + "rows": [ + { + "action": "check-in", + "badge": "", + "device_desc": "Blue Kingston", + "device_id": "USB-1", + "locker_location": "", + "name": "Dana Reyes", + "sanitized": "", + "scanned_viruses": "", + "timestamp": "2026-01-01T00:00:00.000000" + }, + { + "action": "check-out", + "badge": "", + "device_desc": "Blue Kingston", + "device_id": "USB-1", + "locker_location": "", + "name": "Dana Reyes", + "sanitized": "", + "scanned_viruses": "", + "timestamp": "2026-01-01T00:00:00.000000" + }, + { + "action": "check-out", + "badge": "", + "device_desc": "Red SanDisk", + "device_id": "USB-2", + "locker_location": "", + "name": "Sam Okafor", + "sanitized": "", + "scanned_viruses": "", + "timestamp": "2026-01-01T00:00:00.000000" + } + ], + "summary": { + "checkins": 1, + "checkouts": 2, + "devices": 2, + "events": 3, + "people": 2 + } + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + } + ] + }, + { + "surface": "plugin-warranty", + "endpoints": [ + { + "method": "GET", + "path": "/api/warranty", + "auth": "permission:warranty.view", + "params": "not documented", + "purpose": "List warranties. Filters: ?status=, ?assetid=, ?active=false.", + "example": "curl http://localhost:5001/api/warranty -H \"Authorization: Bearer $TOK\"", + "queryparams": [ + { + "name": "active", + "type": "string", + "required": false, + "default": "true" + }, + { + "name": "assetid", + "type": "integer", + "required": false + }, + { + "name": "servicetag", + "type": "string", + "required": false + }, + { + "name": "status", + "type": "string", + "required": false + }, + { + "name": "vendor", + "type": "string", + "required": false + } + ], + "responseexample": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/warranty", + "auth": "permission:warranty.create", + "params": "body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: warranty.create_warranty has no docstring.", + "example": "curl -X POST http://localhost:5001/api/warranty -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "enddate": "2026-01-01", + "servicetag": "", + "vendor": "Acme" + }, + "responseexample": { + "data": { + "assets": [], + "enddate": "2026-01-01", + "isactive": true, + "lastcheckeddate": null, + "notes": null, + "prooffilename": null, + "proofurl": null, + "provider": "manual", + "servicelevel": null, + "servicetag": "", + "startdate": null, + "status": "active", + "statuscolor": "#4CAF50", + "vendor": "Acme", + "warrantyid": 1 + }, + "message": "Warranty created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/warranty/", + "auth": "permission:warranty.delete", + "params": "path: warrantyid (int)", + "purpose": "UNDOCUMENTED: warranty.delete_warranty has no docstring.", + "example": "curl -X DELETE http://localhost:5001/api/warranty/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/warranty/", + "auth": "permission:warranty.view", + "params": "path: warrantyid (int)", + "purpose": "UNDOCUMENTED: warranty.get_warranty has no docstring.", + "example": "curl http://localhost:5001/api/warranty/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "PUT", + "path": "/api/warranty/", + "auth": "permission:warranty.edit", + "params": "path: warrantyid (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: warranty.update_warranty has no docstring.", + "example": "curl -X PUT http://localhost:5001/api/warranty/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "requestexample": { + "enddate": "" + }, + "responseexample": { + "data": { + "assets": [], + "enddate": null, + "isactive": true, + "lastcheckeddate": null, + "notes": null, + "prooffilename": null, + "proofurl": null, + "provider": "manual", + "servicelevel": null, + "servicetag": "", + "startdate": null, + "status": "unknown", + "statuscolor": "#9E9E9E", + "vendor": "Acme", + "warrantyid": 1 + }, + "message": "Warranty updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "DELETE", + "path": "/api/warranty//proof", + "auth": "permission:warranty.edit", + "params": "path: warrantyid (int)", + "purpose": "Remove a warranty's proof document.", + "example": "curl -X DELETE http://localhost:5001/api/warranty/1/proof -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "enddate": null, + "isactive": true, + "lastcheckeddate": null, + "notes": null, + "prooffilename": null, + "proofurl": null, + "provider": "manual", + "servicelevel": null, + "servicetag": "", + "startdate": null, + "status": "unknown", + "statuscolor": "#9E9E9E", + "vendor": "Dell", + "warrantyid": 1 + }, + "message": "Proof removed", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/warranty//proof", + "auth": "permission:warranty.edit", + "params": "path: warrantyid (int); body: JSON (fields not yet documented)", + "purpose": "Upload (or replace) the proof-of-cover document for a warranty.", + "example": "curl -X POST http://localhost:5001/api/warranty/1/proof -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "responseexample": { + "data": { + "enddate": null, + "isactive": true, + "lastcheckeddate": null, + "notes": null, + "prooffilename": "Dell invoice 4471.pdf", + "proofurl": "/api/warranty/proof/warranty-1.pdf", + "provider": "manual", + "servicelevel": null, + "servicetag": "", + "startdate": null, + "status": "unknown", + "statuscolor": "#9E9E9E", + "vendor": "Dell", + "warrantyid": 1 + }, + "message": "Proof uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + }, + "readsbody": true + }, + { + "method": "POST", + "path": "/api/warranty//refresh", + "auth": "permission:warranty.edit", + "params": "path: warrantyid (int); body: JSON (fields not yet documented)", + "purpose": "UNDOCUMENTED: warranty.refresh_warranty has no docstring.", + "example": "curl -X POST http://localhost:5001/api/warranty/1/refresh -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'" + }, + { + "method": "GET", + "path": "/api/warranty/asset/", + "auth": "permission:warranty.view", + "params": "path: assetid (int)", + "purpose": "Warranties covering one asset (for the asset-detail panel).", + "example": "curl http://localhost:5001/api/warranty/asset/1 -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/warranty/dashboard/expiring", + "auth": "permission:warranty.view", + "params": "not documented", + "purpose": "Warranties running out, and ones that already have.", + "example": "curl http://localhost:5001/api/warranty/dashboard/expiring -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "GET", + "path": "/api/warranty/proof/", + "auth": "permission:warranty.view", + "params": "path: filename (path)", + "purpose": "Download a proof document.", + "example": "curl http://localhost:5001/api/warranty/proof/example.png -H \"Authorization: Bearer $TOK\"" + }, + { + "method": "GET", + "path": "/api/warranty/report", + "auth": "permission:warranty.view", + "params": "not documented", + "purpose": "Counts + lists bucketed by derived status.", + "example": "curl http://localhost:5001/api/warranty/report -H \"Authorization: Bearer $TOK\"", + "responseexample": { + "data": { + "buckets": { + "active": [], + "expired": [], + "expiring": [], + "unknown": [] + }, + "counts": { + "active": 0, + "expired": 0, + "expiring": 0, + "unknown": 0 + } + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + }, + { + "method": "POST", + "path": "/api/warranty/sync/dell", + "auth": "permission:warranty.edit", + "params": "body: JSON (fields not yet documented)", + "purpose": "Look up every asset serial (= Dell service tag) that has no warranty yet and create warranties for the ones Dell recognizes.", + "example": "curl -X POST http://localhost:5001/api/warranty/sync/dell -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'", + "queryparams": [ + { + "name": "all", + "type": "string", + "required": false, + "default": "false" + } + ], + "requestexample": {}, + "responseexample": { + "data": { + "candidates": 0, + "created": 0, + "matched": 0, + "updated": 0 + }, + "message": "No PCs need a warranty lookup.", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } + } + ] + } +] \ No newline at end of file diff --git a/docs/openapi.json b/docs/openapi.json index 0dd38f91..58ff11bf 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -280,7 +280,48 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "createddate": "2026-01-01T00:00:00.000000Z", + "displayprefix": "", + "expiresat": null, + "isactive": true, + "isexpired": false, + "lastusedat": null, + "name": "member token", + "resourcescopes": null, + "scopes": null, + "tokenid": 2, + "tokenprefix": "", + "userid": 2, + "username": "" + }, + { + "createddate": "2026-01-01T00:00:00.000000Z", + "displayprefix": "", + "expiresat": null, + "isactive": true, + "isexpired": false, + "lastusedat": null, + "name": "admin token", + "resourcescopes": null, + "scopes": null, + "tokenid": 1, + "tokenprefix": "", + "userid": 1, + "username": "" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -298,7 +339,18 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "all", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "false" + } + } + ] }, "post": { "tags": [ @@ -316,7 +368,37 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "displayprefix": "", + "expiresat": null, + "isactive": true, + "isexpired": false, + "lastusedat": null, + "name": "display svc", + "resourcescopes": [ + "gea-shopfloor-display" + ], + "scopes": [ + "geenforce.fetch" + ], + "secret": "", + "tokenid": 1, + "tokenprefix": "", + "userid": 1, + "warning": "Save this token now. It will not be shown again. Store it somewhere safe." + }, + "message": "Token created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -343,6 +425,15 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "name": "display svc", + "resourcescopes": [ + "gea-shopfloor-display" + ], + "scopes": [ + "geenforce.fetch" + ] } } } @@ -366,7 +457,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "message": "Token revoked", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -415,7 +516,33 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "displayprefix": "", + "expiresat": null, + "isactive": true, + "isexpired": false, + "lastusedat": null, + "name": "test token", + "resourcescopes": null, + "scopes": [ + "applications.edit" + ], + "tokenid": 1, + "tokenprefix": "", + "userid": 1 + }, + "message": "Token updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -455,6 +582,11 @@ "type": "object", "additionalProperties": true, "description": "path: tokenid (int); body: JSON (fields not yet documented)" + }, + "example": { + "scopes": [ + "applications.edit" + ] } } } @@ -478,7 +610,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -496,7 +646,76 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "appname", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "installable", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "showhidden", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "false" + } + } + ] }, "post": { "tags": [ @@ -514,7 +733,45 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "appdescription": null, + "appid": 1, + "applicationlink": null, + "applicationnotes": null, + "appname": "Fleet App", + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "documentationpath": null, + "image": null, + "installersha256": null, + "installpath": null, + "isactive": true, + "ishidden": false, + "isinstallable": false, + "islicenced": false, + "ismonitorable": false, + "isprinter": false, + "isrequired": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "monitorlaunchpath": null, + "monitorprocessname": null, + "policynote": "A configuration policy was created for this application, but it is not attached to any business policy and installs nothing until an admin adds it to one.", + "policyresult": "created", + "supportteamid": null, + "supportteamname": null, + "teamurl": null + }, + "message": "Application created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -541,6 +798,12 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "appname": "Monitored App", + "ismonitorable": true, + "monitorlaunchpath": "C:\\Vendor\\app.exe", + "monitorprocessname": "app.exe" } } } @@ -564,7 +827,24 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "deactivatedpolicies": [ + "Required app: Doomed", + "Hand made push" + ], + "policyresult": "kept" + }, + "message": "Application deleted; deactivated 2 policy(ies) that referenced it: Required app: Doomed, Hand made push", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -595,7 +875,22 @@ "type": "integer" } } - ] + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "This route reads a JSON body. The example is a request that worked." + }, + "example": { + "confirm": "OK" + } + } + } + } }, "get": { "tags": [ @@ -613,7 +908,50 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "appdescription": null, + "appid": 1, + "applicationlink": null, + "applicationnotes": null, + "appname": "TeamApp", + "contacts": [ + { + "name": "Alice", + "sso": "a01" + } + ], + "createddate": "2026-01-01T00:00:00.000000Z", + "documentationpath": null, + "image": null, + "installedcount": 0, + "installersha256": null, + "installpath": null, + "isactive": true, + "ishidden": false, + "isinstallable": false, + "islicenced": false, + "ismonitorable": false, + "isprinter": false, + "isrequired": false, + "knowledgebase": [], + "modifieddate": "2026-01-01T00:00:00.000000Z", + "monitorlaunchpath": null, + "monitorprocessname": null, + "supportteamid": 1, + "supportteamname": "Controls", + "teamurl": "https://sn.example/controls", + "versions": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -662,7 +1000,45 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "appdescription": null, + "appid": 1, + "applicationlink": null, + "applicationnotes": null, + "appname": "Test App", + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "documentationpath": null, + "image": null, + "installersha256": null, + "installpath": null, + "isactive": true, + "ishidden": false, + "isinstallable": false, + "islicenced": false, + "ismonitorable": false, + "isprinter": false, + "isrequired": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "monitorlaunchpath": null, + "monitorprocessname": null, + "policynote": "A configuration policy was created for this application, but it is not attached to any business policy and installs nothing until an admin adds it to one.", + "policyresult": "created", + "supportteamid": null, + "supportteamname": null, + "teamurl": null + }, + "message": "Application updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -702,6 +1078,11 @@ "type": "object", "additionalProperties": true, "description": "path: app_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "ismonitorable": false, + "monitorlaunchpath": "", + "monitorprocessname": "" } } } @@ -725,7 +1106,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -774,7 +1157,43 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "appdescription": null, + "appid": 1, + "applicationlink": null, + "applicationnotes": null, + "appname": "Test App", + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "documentationpath": null, + "image": "/api/applications/image/application-1.png", + "installersha256": null, + "installpath": null, + "isactive": true, + "ishidden": false, + "isinstallable": false, + "islicenced": false, + "ismonitorable": false, + "isprinter": false, + "isrequired": false, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "monitorlaunchpath": null, + "monitorprocessname": null, + "supportteamid": null, + "supportteamname": null, + "teamurl": null + }, + "message": "Application image uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -837,7 +1256,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -888,7 +1309,43 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "appdescription": null, + "appid": 1, + "applicationlink": null, + "applicationnotes": null, + "appname": "Test App", + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "documentationpath": null, + "image": null, + "installersha256": null, + "installpath": "", + "isactive": true, + "ishidden": false, + "isinstallable": false, + "islicenced": false, + "ismonitorable": false, + "isprinter": false, + "isrequired": false, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "monitorlaunchpath": null, + "monitorprocessname": null, + "supportteamid": null, + "supportteamname": null, + "teamurl": null + }, + "message": "Installer removed", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -937,7 +1394,48 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "appdescription": null, + "appid": 1, + "applicationlink": null, + "applicationnotes": null, + "appname": "Test App", + "appversionid": null, + "bytes": 130, + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "detectedversion": "", + "documentationpath": null, + "image": null, + "installersha256": "", + "installpath": "/api/applications/package/1/setup.exe", + "isactive": true, + "ishidden": false, + "isinstallable": false, + "islicenced": false, + "ismonitorable": false, + "isprinter": false, + "isrequired": false, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "monitorlaunchpath": null, + "monitorprocessname": null, + "sha256": "", + "supportteamid": null, + "supportteamname": null, + "teamurl": null, + "uploadedfilename": "setup.exe" + }, + "message": "Installer uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -1000,7 +1498,43 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "appdescription": null, + "appid": 1, + "applicationlink": null, + "applicationnotes": null, + "appname": "Test App", + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "documentationpath": null, + "image": "/api/applications/image/application-1.png", + "installersha256": null, + "installpath": null, + "isactive": true, + "ishidden": false, + "isinstallable": false, + "islicenced": false, + "ismonitorable": false, + "isprinter": false, + "isrequired": false, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "monitorlaunchpath": null, + "monitorprocessname": null, + "supportteamid": null, + "supportteamname": null, + "teamurl": null + }, + "message": "Application reverted to record", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -1040,6 +1574,9 @@ "type": "object", "additionalProperties": true, "description": "path: app_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "confirm": "OK" } } } @@ -1063,7 +1600,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -1112,7 +1651,28 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "appid": 1, + "appversionid": 1, + "dateadded": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "notes": null, + "releasedate": null, + "sha256": "", + "silentargs": "/S /v/qn", + "version": "1.2.3" + }, + "message": "Version created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -1152,6 +1712,11 @@ "type": "object", "additionalProperties": true, "description": "path: app_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "sha256": "", + "silentargs": "/S /v/qn", + "version": "1.2.3" } } } @@ -1171,7 +1736,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -1217,7 +1784,29 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "appdescription": null, + "appid": 1, + "appname": "Test App", + "appversionid": null, + "computerid": 1, + "id": 1, + "installeddate": "2026-01-01T00:00:00.000000Z", + "installedversion": "1.2.3", + "isactive": true + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -1266,7 +1855,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -1329,7 +1920,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -1386,7 +1979,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -1457,7 +2052,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -1509,7 +2106,57 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "assetid": 1, + "assetnumber": "A-1", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "machine", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "mill", + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "serialnumber": "", + "statusid": 1 + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -1527,7 +2174,150 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "businessunit_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "businessunitid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "dir", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "asc" + } + }, + { + "name": "include_type_data", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "false" + } + }, + { + "name": "location_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "locationid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "assetnumber" + } + }, + { + "name": "status_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "statusid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "type", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "type_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "typeid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] }, "post": { "tags": [ @@ -1545,7 +2335,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -1595,7 +2387,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -1644,7 +2438,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -1674,6 +2470,15 @@ "schema": { "type": "integer" } + }, + { + "name": "include_type_data", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } } ] }, @@ -1693,7 +2498,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -1756,7 +2563,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -1807,7 +2616,66 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "incoming": [ + { + "createddate": "2026-01-01T00:00:00.000000Z", + "inheritsposition": true, + "isactive": true, + "isdirectional": true, + "label": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "relationshipid": 1, + "relationshiptypeid": 1, + "relationshiptypename": "controls", + "sourceasset": { + "assetid": 1, + "assetnumber": "PC-1", + "assettypecolor": null, + "assettypeid": 2, + "assettypename": "computer", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "serialnumber": "", + "statusid": 1 + }, + "sourceassetid": 1, + "targetassetid": 2 + } + ], + "outgoing": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -1858,7 +2726,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -1909,7 +2779,115 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assets": [ + { + "assetid": 1, + "assetnumber": "2007", + "assettype": "machine", + "assettypeid": 1, + "businessunit": null, + "businessunitid": null, + "displayname": "", + "levelid": null, + "location": null, + "locationid": null, + "mapx": 100, + "mapy": 100, + "name": null, + "primaryip": null, + "serialnumber": "", + "status": null, + "statuscolor": null, + "statusid": 1, + "typedata": { + "assetid": 1, + "controllermodelid": null, + "controllervendorid": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "islocationonly": false, + "lastmaintenancedate": null, + "machineid": 1, + "machinetypeid": null, + "maintenanceintervaldays": null, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextmaintenancedate": null, + "requiresmanualconfig": false, + "vendorid": null + } + }, + { + "assetid": 2, + "assetnumber": "2008", + "assettype": "machine", + "assettypeid": 1, + "businessunit": null, + "businessunitid": null, + "displayname": "", + "levelid": null, + "location": null, + "locationid": null, + "mapx": 110, + "mapy": 110, + "name": null, + "primaryip": null, + "serialnumber": "", + "status": null, + "statuscolor": null, + "statusid": 1, + "typedata": { + "assetid": 2, + "controllermodelid": null, + "controllervendorid": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "islocationonly": false, + "lastmaintenancedate": null, + "machineid": 2, + "machinetypeid": null, + "maintenanceintervaldays": null, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextmaintenancedate": null, + "requiresmanualconfig": false, + "vendorid": null + } + } + ], + "filters": { + "assettypes": [ + { + "assettype": "machine", + "assettypeid": 1, + "color": null, + "icon": null + } + ], + "businessunits": [], + "locations": [], + "statuses": [], + "subtypes": { + "Computer": [], + "Machine": [], + "Measuring Tool": [], + "Network Device": [], + "Printer": [] + } + }, + "total": 2 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -1927,7 +2905,57 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "assettype", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "businessunitid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "locationid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "statusid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "subtype", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] } }, "/api/assets/relationships": { @@ -1947,7 +2975,44 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "inheritsposition": true, + "isactive": true, + "label": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "propagated": [ + { + "createddate": "2026-01-01T00:00:00.000000Z", + "inheritsposition": true, + "isactive": true, + "label": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "relationshipid": 3, + "relationshiptypeid": 1, + "sourceassetid": 1, + "targetassetid": 3 + } + ], + "propagatedcount": 1, + "relationshipid": 2, + "relationshiptypeid": 1, + "sourceassetid": 1, + "targetassetid": 2 + }, + "message": "Relationship created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -1974,6 +3039,11 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "relationshiptypeid": 1, + "sourceassetid": 1, + "targetassetid": 2 } } } @@ -1997,7 +3067,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "message": "Relationship deleted", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -2048,7 +3128,26 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "color": null, + "description": null, + "isdirectional": false, + "propagatesthrough": [], + "relationshiptype": "WiFi", + "relationshiptypeid": 1 + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -2084,7 +3183,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "color": null, + "description": null, + "isdirectional": true, + "propagatesthrough": [], + "relationshiptype": "Backup For", + "relationshiptypeid": 1 + }, + "message": "Relationship type created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -2111,6 +3228,10 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "isdirectional": false, + "relationshiptype": "Dualpath" } } } @@ -2134,7 +3255,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -2183,7 +3306,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "color": null, + "description": null, + "isdirectional": false, + "propagatesthrough": [], + "relationshiptype": "USB", + "relationshiptypeid": 1 + }, + "message": "Relationship type updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -2223,6 +3364,9 @@ "type": "object", "additionalProperties": true, "description": "path: type_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "isdirectional": false } } } @@ -2246,7 +3390,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -2264,7 +3426,43 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] }, "post": { "tags": [ @@ -2282,7 +3480,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -2332,7 +3532,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -2381,7 +3583,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -2430,7 +3634,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -2493,7 +3699,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -2511,7 +3735,43 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] }, "post": { "tags": [ @@ -2529,7 +3789,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -2579,7 +3841,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -2628,7 +3892,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -2691,7 +3957,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 50, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -2709,7 +3993,75 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "action", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "entitytype", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "from_date", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 50 + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "to_date", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "userid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] } }, "/api/auditlogs/entity/{entitytype}/{entityid}": { @@ -2729,7 +4081,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -2788,7 +4142,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -2838,7 +4194,23 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "expired": 0, + "newest": "2026-01-01T00:00:00.000000", + "oldest": "2026-01-01T00:00:00.000000", + "retentiondays": 30, + "total": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -2876,7 +4248,22 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "actions": {}, + "activeUsers": [], + "entities": {}, + "recentCount": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -2914,7 +4301,20 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "mustchangepassword": "" + }, + "message": "Password changed", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -2941,6 +4341,10 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "current_password": "", + "new_password": "" } } } @@ -2960,7 +4364,39 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "access_token": "", + "expires_in": 3600, + "refresh_token": "", + "token_type": "", + "user": { + "email": "", + "firstname": "", + "lastname": "", + "mustchangepassword": "", + "permissions": [ + "endpoints.runscript", + "endpoints.agentupdate", + "endpoints.view", + "" + ], + "roles": [ + "upgradeoperatorrole" + ], + "userid": 2, + "username": "" + } + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -2981,6 +4417,10 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "password": "", + "username": "" } } } @@ -3004,7 +4444,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -3054,7 +4496,33 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "email": "", + "firstname": "", + "lastname": "", + "mustchangepassword": "", + "permissions": [ + "assets.view", + "assets.create", + "assets.edit", + "" + ], + "roles": [ + "admin" + ], + "userid": 1, + "username": "" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -3092,7 +4560,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -3143,7 +4613,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -3155,7 +4643,59 @@ } } } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "businessunit", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] }, "post": { "tags": [ @@ -3173,7 +4713,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -3223,7 +4765,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -3272,7 +4816,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -3321,7 +4867,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -3384,7 +4932,40 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "accessprotocols": [], + "action": "updated", + "assetid": 2, + "identityvalue": "LEGACYPC", + "machinelinks": [], + "measuringtoollinkcount": 0, + "measuringtoollinks": [], + "partmarkers": [ + { + "assetid": 3, + "assetnumber": "LEGACYPC-PARTMARKER", + "operationassetid": null + } + ], + "printerlinkcount": 0, + "printerlinks": [], + "status": "ok", + "warnings": [ + "mapped ComputerType not found: Shopfloor PC", + "machine number '0615' is this PC's own asset number; marker not filed under an operation. Rename the PC asset to its hostname, or create the operation asset." + ] + }, + "message": "computers collector updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -3424,6 +5005,32 @@ "type": "object", "additionalProperties": true, "description": "path: pluginname (string); body: JSON (fields not yet documented)" + }, + "example": { + "hostname": "", + "queues": [ + { + "drivername": "HP Universal Printing PS", + "isdefault": true, + "portaddress": "", + "portname": "IP_10.20.0.11", + "queuename": "PRINTER-A" + }, + { + "drivername": "HP Universal Printing PS", + "isdefault": false, + "portaddress": "", + "portname": "IP_10.20.0.12", + "queuename": "PRINTER-B" + }, + { + "drivername": "HP Universal Printing PS", + "isdefault": false, + "portaddress": "", + "portname": "IP_10.20.0.99", + "queuename": "Reception Copier" + } + ] } } } @@ -3447,7 +5054,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -3485,7 +5094,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -3535,7 +5146,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "errors": [], + "notfound": [ + "WJLEG01" + ], + "timestamp": "2026-01-01T00:00:00.000000", + "updated": 0 + }, + "message": "0 PC(s) updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -3562,6 +5191,13 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "pcs": [ + { + "hostname": "" + } + ] } } } @@ -3585,7 +5221,24 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "notfound": [ + "WJLEG01" + ], + "timestamp": "2026-01-01T00:00:00.000000", + "updated": 0 + }, + "message": "0 PC(s) heartbeat recorded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -3612,6 +5265,9 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "hostname": "" } } } @@ -3635,7 +5291,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -3685,7 +5343,26 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "endpoints": [ + "POST /api/collector/", + "GET /api/collector/_schemas", + "POST /api/collector/pc", + "" + ], + "status": "ok", + "timestamp": "2026-01-01T00:00:00.000000" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -3723,7 +5400,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -3741,7 +5428,26 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "assettypeid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] }, "post": { "tags": [ @@ -3759,7 +5465,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -3809,7 +5517,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -3858,7 +5568,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -3921,7 +5633,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -3970,7 +5684,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -4033,7 +5749,64 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "activeassets": 14, + "bystatus": { + "In Use": 14 + }, + "counts": { + "machines": 0, + "measuringtools": 14, + "networkdevices": 0, + "pcs": 0, + "printers": 0, + "total": 14 + }, + "countsactiveonly": true, + "dualpathcollapsed": true, + "inactiveexcluded": 0, + "inrepair": 0, + "recent": [ + { + "assetid": 14, + "assetnumber": "MT-MAP-TD", + "assettype": "measuring_tool", + "createddate": "2026-01-01T00:00:00.000000Z" + }, + { + "assetid": 13, + "assetnumber": "MT-MAP-MIC", + "assettype": "measuring_tool", + "createddate": "2026-01-01T00:00:00.000000Z" + }, + { + "assetid": 12, + "assetnumber": "MT-MAP-CAL", + "assettype": "measuring_tool", + "createddate": "2026-01-01T00:00:00.000000Z" + }, + "" + ], + "statuses": { + "In Use": 14 + }, + "totalassets": 14, + "totalmachines": 0, + "totalmeasuringtool": 14, + "totalnetwork": 0, + "totalpc": 0, + "totalprinter": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -4072,7 +5845,19 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "cansearch": false + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -4100,7 +5885,20 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "database": "healthy", + "status": "ok" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -4133,7 +5931,41 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "icon": "layout-dashboard", + "name": "Dashboard", + "permission": "assets.view", + "position": 0, + "route": "/" + }, + { + "icon": "map", + "name": "Map", + "permission": "assets.view", + "position": 4, + "route": "/map" + }, + { + "icon": "bell", + "name": "Notifications", + "permission": "notifications.view", + "plugin": "notifications", + "position": 5, + "route": "/notifications" + }, + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -4165,7 +5997,19 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "bytype": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -4203,7 +6047,40 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "activeassets": 0, + "bystatus": {}, + "counts": { + "machines": 0, + "measuringtools": 0, + "networkdevices": 0, + "pcs": 0, + "printers": 0, + "total": 0 + }, + "countsactiveonly": true, + "dualpathcollapsed": true, + "inactiveexcluded": 0, + "inrepair": 0, + "recent": [], + "statuses": {}, + "totalassets": 0, + "totalmachines": 0, + "totalmeasuringtool": 0, + "totalnetwork": 0, + "totalpc": 0, + "totalprinter": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -4241,7 +6118,95 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "empty": "hide", + "endpoint": "/api/geenforce/dashboard/failures", + "id": "geenforce-failures", + "map": { + "detail": "entryname", + "link": "/pcs/{computerid}", + "meta": [ + { + "key": "message" + }, + { + "key": "exitcode", + "label": "exit" + } + ], + "timestamp": "receivedat", + "title": "hostname" + }, + "permission": "geenforce.manage", + "plugin": "geenforce", + "position": 10, + "render": "exceptions", + "severity": "critical", + "shortname": "Enforcement", + "title": "Enforcement failures", + "viewall": "/geenforce" + }, + { + "empty": "hide", + "endpoint": "/api/computers/dashboard/sharedmachines", + "id": "computers-sharedmachines", + "map": { + "detail": "pcs", + "link": "/machines/{assetid}", + "meta": [ + { + "key": "pccount", + "suffix": " PCs" + } + ], + "title": "machinenumber" + }, + "permission": "computers.view", + "plugin": "computers", + "position": 15, + "render": "exceptions", + "severity": "critical", + "shortname": "Duplicate machine no.", + "title": "Machine numbers on two PCs", + "viewall": "/machines" + }, + { + "empty": "hide", + "endpoint": "/api/endpoints/dashboard/stability", + "id": "endpoints-stability", + "map": { + "detail": "", + "link": "/pcs/{computerid}", + "meta": [ + { + "key": "eventcount", + "suffix": " events (48h)" + } + ], + "title": "hostname" + }, + "permission": "endpoints.fleet", + "plugin": "endpoints", + "position": 16, + "render": "exceptions", + "severity": "critical", + "shortname": "Crashes", + "title": "Endpoints with crashes or shutdowns", + "viewall": "/pcs" + }, + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -4279,7 +6244,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -4315,7 +6290,30 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "businessunit": "Materials", + "businessunitid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "dashboarddefaultid": 1, + "description": "Materials lobby kiosk", + "displaypath": "/shopfloor", + "displayrole": "Dashboard", + "fqdn": "", + "ipaddress": "", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z" + }, + "message": "Mapping created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -4342,6 +6340,11 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "businessunitid": 1, + "description": "Materials lobby kiosk", + "ipaddress": "" } } } @@ -4365,7 +6368,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "message": "Mapping deleted", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -4414,7 +6427,30 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "businessunit": "Materials", + "businessunitid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "dashboarddefaultid": 1, + "description": "moved", + "displaypath": "/shopfloor", + "displayrole": "Dashboard", + "fqdn": "", + "ipaddress": "", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z" + }, + "message": "Mapping updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -4454,6 +6490,9 @@ "type": "object", "additionalProperties": true, "description": "path: default_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "description": "moved" } } } @@ -4473,7 +6512,24 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "businessunit": null, + "businessunitid": null, + "fqdn": "", + "ipaddress": "", + "path": "/parts-kiosk", + "role": "3DPrintRoom" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -4485,7 +6541,25 @@ } } } - } + }, + "parameters": [ + { + "name": "fqdn", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "ipaddress", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/dashboarddefaults/visitor-location": { @@ -4501,7 +6575,22 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "businessunit": "Materials", + "businessunitid": 1, + "fqdn": "", + "ipaddress": "" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -4513,7 +6602,25 @@ } } } - } + }, + "parameters": [ + { + "name": "fqdn", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "ipaddress", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/docs": { @@ -4529,7 +6636,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -4557,7 +6666,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -4585,7 +6696,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -4613,7 +6726,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -4645,7 +6760,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -4663,7 +6796,59 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "locationname", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] }, "post": { "tags": [ @@ -4681,7 +6866,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -4731,7 +6918,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -4780,7 +6969,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -4829,7 +7020,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -4892,7 +7085,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -4910,7 +7113,18 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + } + ] }, "post": { "tags": [ @@ -4928,7 +7142,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -4978,7 +7194,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -5027,7 +7245,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -5091,7 +7311,48 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "buildings": [ + { + "buildingid": 1, + "buildingname": "Public Works", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "levels": [ + { + "assetcount": 0, + "blueprintdark": null, + "blueprintlight": "floorplan.svg", + "buildingid": 1, + "buildingname": "Public Works", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "isdefault": false, + "levelid": 1, + "levelname": "Ground", + "mapheight": 2550, + "mapwidth": 3300, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "sortorder": 1 + } + ], + "modifieddate": "2026-01-01T00:00:00.000000Z", + "sortorder": 1 + } + ], + "defaultlevelid": 1, + "totalplaced": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -5121,7 +7382,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -5171,7 +7434,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -5221,7 +7486,31 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "blueprintdark": null, + "blueprintlight": "floorplan.svg", + "buildingid": 1, + "buildingname": "Public Works", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "isdefault": false, + "levelid": 1, + "levelname": "Ground", + "mapheight": 2550, + "mapwidth": 3300, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "sortorder": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -5264,7 +7553,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -5325,7 +7616,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -5388,7 +7681,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -5452,7 +7747,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -5506,7 +7803,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -5556,7 +7855,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -5617,7 +7918,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -5680,7 +7983,21 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "snapshotid": 1, + "updated": 1 + }, + "message": "1 position(s) saved", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -5707,6 +8024,16 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "positions": [ + { + "assetid": 1, + "levelid": 2, + "mapx": 42, + "mapy": 43 + } + ] } } } @@ -5730,7 +8057,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -5768,7 +8105,21 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "restored": 3, + "skippedassetids": [] + }, + "message": "3 marker(s) restored", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -5831,7 +8182,63 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetcount": 3, + "dryrun": false, + "levelid": 1, + "moves": [ + { + "assetid": 1, + "assetnumber": "M000", + "fromx": 100, + "fromy": 100, + "name": null, + "outofbounds": false, + "tox": 100, + "toy": 1550 + }, + { + "assetid": 2, + "assetnumber": "M001", + "fromx": 1500, + "fromy": 1200, + "name": null, + "outofbounds": true, + "tox": 1500, + "toy": 2650 + }, + { + "assetid": 3, + "assetnumber": "M002", + "fromx": 3200, + "fromy": 2500, + "name": null, + "outofbounds": true, + "tox": 3200, + "toy": 3950 + } + ], + "outofboundscount": 2, + "snapshotid": 1, + "tolevelid": 1, + "transform": { + "offsetx": 0.0, + "offsety": 1450.0, + "scalex": 1.0, + "scaley": 1.0 + } + }, + "message": "3 marker(s) moved; snapshot 1 can restore them", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -5858,6 +8265,24 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "dryrun": false, + "landmarks": [ + { + "fromx": 0, + "fromy": 0, + "tox": 0, + "toy": 1450 + }, + { + "fromx": 1000, + "fromy": 1000, + "tox": 1000, + "toy": 2450 + } + ], + "levelid": 1 } } } @@ -5881,7 +8306,20 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "updated": 1 + }, + "message": "1 marker(s) unverified", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -5908,6 +8346,12 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "assetids": [ + 1 + ], + "unverify": true } } } @@ -5931,7 +8375,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -5949,7 +8411,83 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "category", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "modelnumber", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "modeltype", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "vendor", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] }, "post": { "tags": [ @@ -5967,7 +8505,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -6017,7 +8557,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -6066,7 +8608,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -6115,7 +8659,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -6178,7 +8724,30 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "documentationurl": null, + "imageurl": null, + "isactive": true, + "modelnumber": "TESTMODEL-1", + "modelnumberid": 1, + "modeltypeid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "vendorid": null + }, + "message": "Model image removed", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -6227,7 +8796,30 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "documentationurl": null, + "imageurl": "/api/models/image/model-1.jpg", + "isactive": true, + "modelnumber": "TESTMODEL-1", + "modelnumberid": 1, + "modeltypeid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "vendorid": null + }, + "message": "Model image uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -6286,7 +8878,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -6332,7 +8926,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -6350,7 +8962,67 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "category", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "modeltype", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] }, "post": { "tags": [ @@ -6368,7 +9040,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -6418,7 +9092,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -6467,7 +9143,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -6516,7 +9194,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -6579,7 +9259,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -6597,7 +9295,67 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "osname", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "osversion", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] }, "post": { "tags": [ @@ -6615,7 +9373,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -6665,7 +9425,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -6714,7 +9476,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -6763,7 +9527,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -6826,7 +9592,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -6864,7 +9632,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -6928,7 +9698,22 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + "backups", + "computers", + "employees", + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -6960,7 +9745,34 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "routes": [ + { + "component": "drawn.vue", + "hub": "model-catalog", + "name": "drawn-route", + "path": "drawn", + "tab": "Drawn" + }, + { + "component": ".plugins-staged/computers/views/OperatingSystemsList.vue", + "hub": "model-catalog", + "name": "model-catalog-os", + "path": "operatingsystems", + "tab": "Operating Systems" + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -6998,7 +9810,122 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "assettypes": [ + "computer" + ], + "empty": "No agent report yet.", + "endpoint": "/api/endpoints/asset/{assetid}/status", + "group": "details", + "id": "endpoints-status", + "plugin": "endpoints", + "position": 10, + "render": "keyvalue", + "title": "Endpoint status" + }, + { + "assettypes": [ + "computer" + ], + "collapsible": true, + "commandendpoint": "/api/endpoints/{assetid}/commands", + "empty": "No monitored services reported.", + "endpoint": "/api/endpoints/asset/{assetid}/services", + "facets": { + "key": "statelabel", + "order": [ + "Running", + "Stopped", + "Paused", + "" + ] + }, + "filter": { + "equals": "service", + "key": "kind" + }, + "group": "serviceprocess", + "id": "endpoints-tracked-services", + "map": { + "action": "action", + "badge": { + "color": "statecolor", + "label": "statelabel" + }, + "title": "name" + }, + "permission": "endpoints.command", + "plugin": "endpoints", + "position": 12, + "render": "list", + "status": { + "key": "statecolor", + "levels": { + "green": "ok", + "red": "critical", + "yellow": "warning" + } + }, + "title": "Tracked Services" + }, + { + "assettypes": [ + "computer" + ], + "collapsible": true, + "commandendpoint": "/api/endpoints/{assetid}/commands", + "empty": "No monitored processes reported.", + "endpoint": "/api/endpoints/asset/{assetid}/services", + "facets": { + "key": "statelabel", + "order": [ + "Running", + "Stopped", + "Paused", + "" + ] + }, + "filter": { + "equals": "process", + "key": "kind" + }, + "group": "serviceprocess", + "id": "endpoints-tracked-processes", + "map": { + "action": "action", + "badge": { + "color": "statecolor", + "label": "statelabel" + }, + "title": "name" + }, + "permission": "endpoints.command", + "plugin": "endpoints", + "position": 13, + "render": "list", + "status": { + "key": "statecolor", + "levels": { + "green": "ok", + "red": "critical", + "yellow": "warning" + } + }, + "title": "Tracked Processes" + }, + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7016,7 +9943,17 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "assetid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] } }, "/api/pluginui/asset-presentation": { @@ -7036,7 +9973,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "assettype": "measuring_tool", + "icon": "ruler", + "label": "Measuring Tool", + "plugin": "measuringtools", + "route": "/measuringtools/by-asset/{assetid}" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7074,7 +10029,52 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "assettype": "computer", + "edit_permission": "computers.manage", + "optional_tabs": [], + "plugin": "computers", + "subtype_color_field": "color", + "subtype_description_field": "description", + "subtype_endpoint": "/computers/types", + "subtype_id_field": "computertypeid", + "subtype_label_field": "computertype" + }, + { + "assettype": "machine", + "edit_permission": "machines.manage", + "optional_tabs": [], + "plugin": "machines", + "subtype_color_field": "color", + "subtype_description_field": "description", + "subtype_endpoint": "/machines/types", + "subtype_id_field": "machinetypeid", + "subtype_label_field": "machinetype" + }, + { + "assettype": "measuring_tool", + "edit_permission": "measuringtools.manage", + "optional_tabs": [], + "plugin": "measuringtools", + "subtype_color_field": "color", + "subtype_description_field": "description", + "subtype_endpoint": "/measuringtools/types", + "subtype_id_field": "measuringtooltypeid", + "subtype_label_field": "name" + }, + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7112,7 +10112,45 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "category": "location", + "edit_permission": "admin", + "group": "other", + "optional_tabs": [], + "subtype_color_field": "color", + "subtype_description_field": "description", + "subtype_endpoint": "/locations/types", + "subtype_id_field": "locationtypeid", + "subtype_label_field": "locationtype", + "type_name": "Location Type", + "type_name_plural": "Location Types" + }, + { + "category": "notification", + "custom_component": "NotificationTypesTab", + "edit_permission": "admin", + "group": "other", + "optional_tabs": [], + "subtype_color_field": "typecolor", + "subtype_description_field": "typedescription", + "subtype_endpoint": "/notifications/types", + "subtype_id_field": "notificationtypeid", + "subtype_label_field": "typename", + "type_name": "Notification Type", + "type_name_plural": "Notification Types" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7150,7 +10188,26 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "endpoint": "/api/measuringtools/map-overlay", + "id": "calibration-due", + "label": "Calibration due", + "legend": true, + "plugin": "measuringtools", + "style": "badge" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7188,7 +10245,46 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "description": "Collection interval, share root for part-marker snapshots, staleness threshold and revision retention", + "group": "Machines", + "icon": "package", + "plugin": "backups", + "position": 21, + "title": "Backups", + "to": "/settings/backups" + }, + { + "description": "Download the collector script stamped with this site's URL and ranges", + "group": "Computers", + "icon": "download", + "plugin": "computers", + "position": 26, + "title": "Asset reporter", + "to": "/settings/collector" + }, + { + "description": "The shared agent key PCs use until they enroll, the site agent installer builder, and what the agent reports.", + "group": "Endpoints", + "icon": "key", + "plugin": "endpoints", + "position": 27, + "title": "Endpoints", + "to": "/settings/endpoints/agentkey" + }, + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7226,7 +10322,43 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "reports": [ + { + "category": "inventory", + "description": "Machine count grouped by machine type", + "endpoint": "/api/reports/machines-by-type", + "id": "machines-by-type", + "name": "Machines by Type" + }, + { + "category": "inventory", + "description": "Asset count grouped by status", + "endpoint": "/api/reports/assets-by-status", + "id": "assets-by-status", + "name": "Assets by Status" + }, + { + "category": "usage", + "description": "Most clicked knowledge base articles", + "endpoint": "/api/reports/kb-popularity", + "id": "kb-popularity", + "name": "KB Popularity" + }, + "" + ], + "total": 13 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7264,7 +10396,26 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "data": { + "bylocation": [], + "bystatus": [], + "bytype": [] + }, + "generated": "2026-01-01T00:00:00.000000", + "report": "asset_inventory", + "total": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7282,7 +10433,33 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "businessunitid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "format", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "locationid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] } }, "/api/reports/assets-by-status": { @@ -7302,7 +10479,22 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "data": [], + "generated": "2026-01-01T00:00:00.000000", + "report": "assets_by_status", + "total": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7320,7 +10512,33 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "assettypeid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "businessunitid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "format", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/reports/email": { @@ -7340,7 +10558,21 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "reason": "notconfigured", + "sent": false + }, + "message": "Email is not configured (SMTP disabled or host unset).", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7367,6 +10599,21 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "columns": [ + { + "key": "vendor", + "label": "Vendor" + } + ], + "rows": [ + { + "vendor": "Dell" + } + ], + "subject": "Warranty Report", + "to": "" } } } @@ -7390,7 +10637,22 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "data": [], + "generated": "2026-01-01T00:00:00.000000", + "report": "kb_popularity", + "total": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7408,7 +10670,26 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "format", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 20 + } + } + ] } }, "/api/reports/machines-by-type": { @@ -7428,7 +10709,22 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "data": [], + "generated": "2026-01-01T00:00:00.000000", + "report": "machines_by_type", + "total": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7446,7 +10742,25 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "businessunitid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "format", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/reports/pc-relationships": { @@ -7466,7 +10780,30 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "data": [ + { + "hostname": "", + "ip": "", + "machine_number": "4242", + "model": "", + "vendor": "" + } + ], + "generated": "2026-01-01T00:00:00.000000", + "report": "pc_relationships", + "total": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7484,7 +10821,17 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "format", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/reports/software-compliance": { @@ -7504,7 +10851,22 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "data": [], + "generated": "2026-01-01T00:00:00.000000", + "message": "No required applications defined", + "report": "software_compliance" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7522,7 +10884,25 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "appid", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "format", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/scripts": { @@ -7542,7 +10922,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7560,7 +10950,26 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] }, "post": { "tags": [ @@ -7578,7 +10987,32 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "captureoutput": false, + "createddate": "2026-01-01T00:00:00.000000Z", + "errormode": "default", + "filename": null, + "hasbody": false, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "runcontext": "system", + "scriptdescription": null, + "scriptid": 1, + "scriptname": "autologon", + "sha256": null, + "workingdirectory": "C:\\Program Files\\Sysinternals" + }, + "message": "Script created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7605,6 +11039,10 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "scriptname": "autologon", + "workingdirectory": "C:\\Program Files\\Sysinternals" } } } @@ -7628,7 +11066,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -7677,7 +11117,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -7726,7 +11168,32 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "captureoutput": true, + "createddate": "2026-01-01T00:00:00.000000Z", + "errormode": "default", + "filename": null, + "hasbody": false, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "runcontext": "system", + "scriptdescription": "now documented", + "scriptid": 1, + "scriptname": "keep-my-flag", + "sha256": null, + "workingdirectory": null + }, + "message": "Script updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7766,6 +11233,9 @@ "type": "object", "additionalProperties": true, "description": "path: script_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "scriptdescription": "now documented" } } } @@ -7789,7 +11259,34 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "bytes": 23, + "captureoutput": false, + "createddate": "2026-01-01T00:00:00.000000Z", + "errormode": "default", + "filename": "script-1.ps1", + "hasbody": true, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "runcontext": "system", + "scriptdescription": null, + "scriptid": 1, + "scriptname": "digest-check", + "sha256": "", + "uploadedfilename": "digest-check.ps1", + "workingdirectory": null + }, + "message": "Script body uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7852,7 +11349,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -7904,7 +11403,47 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "counts": { + "knowledgebase": 2 + }, + "degraded": [ + "Employee" + ], + "query": "zzkeyword", + "results": [ + { + "id": 1, + "linkurl": "u", + "relevance": 25.0, + "subtitle": "Livesearchapp", + "title": "Zzrunbook for the live one", + "type": "knowledgebase", + "url": "/knowledgebase/1" + }, + { + "id": 3, + "linkurl": "u", + "relevance": 25.0, + "subtitle": null, + "title": "Zzrunbook with no topic", + "type": "knowledgebase", + "url": "/knowledgebase/3" + } + ], + "total": 2, + "total_all": 2 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7922,7 +11461,18 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "" + } + } + ] } }, "/api/search/suggest": { @@ -7942,7 +11492,20 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "query": "keyecne", + "suggestion": "keyence" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7960,7 +11523,18 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "" + } + } + ] } }, "/api/settings": { @@ -7981,7 +11555,49 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "category": "auth", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "SAML Assertion Consumer Service URL", + "key": "saml_acs_url", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "settingid": 111, + "value": "", + "valuetype": "string" + }, + { + "category": "auth", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "SAML group name that grants admin role", + "key": "saml_admin_group", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "settingid": 114, + "value": "", + "valuetype": "string" + }, + { + "category": "auth", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "Allow local username/password login when SAML is enabled", + "key": "saml_allow_local_login", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "settingid": 112, + "value": true, + "valuetype": "boolean" + }, + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -7993,7 +11609,17 @@ } } } - } + }, + "parameters": [ + { + "name": "category", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] }, "post": { "tags": [ @@ -8011,7 +11637,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -8062,7 +11690,26 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "category": "plugin.endpoints", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "Accept a one-line install bootstrap token in the ?t= query string as well as the X-Endpoint-Bootstrap-Token header. On (the default) keeps the pasted install commands and the Download .ps1 link workin...", + "key": "plugin.endpoints.bootstrapquerytoken", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "settingid": 1, + "value": false, + "valuetype": "boolean" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -8105,7 +11752,27 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "category": "plugin.endpoints", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "Accept a one-line install bootstrap token in the ?t= query string as well as the X-Endpoint-Bootstrap-Token header. On (the default) keeps the pasted install commands and the Download .ps1 link workin...", + "key": "plugin.endpoints.bootstrapquerytoken", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "settingid": 1, + "value": false, + "valuetype": "boolean" + }, + "message": "Setting updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -8145,6 +11812,9 @@ "type": "object", "additionalProperties": true, "description": "path: key (string); body: JSON (fields not yet documented)" + }, + "example": { + "value": "" } } } @@ -8168,7 +11838,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -8218,7 +11890,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -8256,7 +11930,33 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "tables": [ + { + "filename": "assetstatuses.csv", + "table": "assetstatuses" + }, + { + "filename": "assettypes.csv", + "table": "assettypes" + }, + { + "filename": "locationtypes.csv", + "table": "locationtypes" + }, + "" + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -8294,7 +11994,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -8345,7 +12047,29 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "batches": [ + { + "batchid": "", + "createdat": "2026-01-01 00:00:00.000000", + "createdby": "", + "kind": "winc-machine-adoption", + "rowcount": 1, + "status": "undone", + "undoneat": "2026-01-01 00:00:00.000000" + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -8383,7 +12107,46 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "batchid": "", + "blocking": false, + "committed": true, + "kind": "winc-machine-adoption", + "needscreate": { + "businessunit": { + "Large Parts": 1 + } + }, + "planhash": "", + "rowcount": 1, + "summary": { + "ambiguous_match": 0, + "businessunit_blank": 0, + "businessunit_needs-create": 1, + "businessunit_resolved": 0, + "businessunit_unresolved": 0, + "export_rows": 2, + "in_scope": 1, + "location_blank": 0, + "location_needs-create": 0, + "location_resolved": 1, + "location_unresolved": 0, + "non_workstation_rows": 1, + "skipped_no_existing_machine": 0 + }, + "uncreatable": {}, + "unresolved": {} + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -8433,7 +12196,52 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "blocking": true, + "committed": false, + "kind": "winc-machine-adoption", + "needscreate": { + "businessunit": { + "Large Parts": 1 + }, + "location": { + "T6": 1 + } + }, + "planhash": "", + "rowcount": 1, + "summary": { + "ambiguous_match": 0, + "businessunit_blank": 0, + "businessunit_needs-create": 1, + "businessunit_resolved": 0, + "businessunit_unresolved": 0, + "export_rows": 1, + "in_scope": 1, + "location_blank": 0, + "location_needs-create": 1, + "location_resolved": 0, + "location_unresolved": 0, + "non_workstation_rows": 0, + "skipped_no_existing_machine": 0 + }, + "uncreatable": { + "location": { + "T6": 1 + } + }, + "unresolved": {} + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -8483,7 +12291,29 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "batchid": "", + "conflicts": [ + { + "column": null, + "operation": "insert", + "reason": "still referenced by dashboarddefaults.businessunitid", + "table": "businessunits", + "targetid": 1 + } + ], + "reverted": 3 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -8510,6 +12340,9 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "batchid": "" } } } @@ -8533,7 +12366,21 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "key": "site_favicon", + "value": "/api/settings/branding/logo-favicon.ico" + }, + "message": "Logo uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -8579,7 +12426,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -8625,7 +12474,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -8671,7 +12522,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -8717,7 +12570,20 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "created": 112 + }, + "message": "112 default settings created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -8744,7 +12610,8 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" - } + }, + "example": {} } } } @@ -8767,7 +12634,21 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "error": "[Errno -2] Name or service not known", + "sent": false + }, + "message": "Test email failed: [Errno -2] Name or service not known", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -8794,6 +12675,9 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "to": "" } } } @@ -8817,7 +12701,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -8863,7 +12749,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -8903,7 +12791,19 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "needsadmin": false + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -8935,7 +12835,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -8985,7 +12887,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -9035,7 +12939,24 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "count": 0, + "lastgoodat": null, + "requestid": "00000000", + "retention": { + "keepdaily": 5, + "keepmonthly": 6, + "keepweekly": 4 + }, + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -9071,7 +12992,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -9121,7 +13044,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -9172,7 +13097,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -9235,7 +13162,21 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "queued": true, + "snapshotid": "wj-2026-09-06T120000Z" + }, + "message": "Schema upgrade queued.", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -9285,7 +13226,20 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "backups": [], + "restores": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -9323,7 +13277,32 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "backupsroot": "/backups", + "destinationkind": "local", + "destinationkinds": [ + "local", + "smb", + "aws" + ], + "fields": {}, + "isarmed": true, + "retention": { + "keepdaily": 5, + "keepmonthly": 6, + "keepweekly": 4 + } + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -9359,7 +13338,32 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "backupsroot": "/backups", + "destinationkind": "local", + "destinationkinds": [ + "local", + "smb", + "aws" + ], + "fields": {}, + "isarmed": true, + "retention": { + "keepdaily": 5, + "keepmonthly": 6, + "keepweekly": 4 + } + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -9386,7 +13390,8 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" - } + }, + "example": {} } } } @@ -9409,7 +13414,20 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "backup": null, + "restore": null + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -9447,7 +13465,28 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": false, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "supportteamid": 1, + "teamname": "Retired", + "teamurl": null, + "webhookurl": null + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -9465,7 +13504,26 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "teamname", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] }, "post": { "tags": [ @@ -9483,7 +13541,27 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "supportteamid": 1, + "teamname": "Controls", + "teamurl": "https://servicenow.example/group/controls", + "webhookurl": null + }, + "message": "Support team created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -9510,6 +13588,11 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "createddate": "2026-01-01 00:00:00", + "modifieddate": "2026-01-01T00:00:00", + "teamname": "Legacy" } } } @@ -9533,7 +13616,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "message": "Support team deleted", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -9582,7 +13675,47 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "contacts": [ + { + "contactid": 2, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "First", + "sortorder": 1, + "sso": "111", + "supportteamid": 1 + }, + { + "contactid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Second", + "sortorder": 2, + "sso": "222", + "supportteamid": 1 + } + ], + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "supportteamid": 1, + "teamname": "Controls", + "teamurl": null, + "webhookurl": null + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -9631,7 +13764,27 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "contacts": [], + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "supportteamid": 1, + "teamname": "Controls Renamed", + "teamurl": "https://x.example", + "webhookurl": null + }, + "message": "Support team updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -9671,6 +13824,10 @@ "type": "object", "additionalProperties": true, "description": "path: team_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "teamname": "Controls Renamed", + "teamurl": "https://x.example" } } } @@ -9694,7 +13851,27 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "contactid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Second", + "sortorder": 2, + "sso": "222", + "supportteamid": 1 + }, + "message": "Contact created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -9734,6 +13911,11 @@ "type": "object", "additionalProperties": true, "description": "path: team_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "createddate": "2026-01-01 00:00:00", + "modifieddate": "2026-01-01T00:00:00", + "name": "LegacyOwner" } } } @@ -9757,7 +13939,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "message": "Contact deleted", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -9814,7 +14006,27 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "contactid": 2, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "First Updated", + "sortorder": 1, + "sso": "111", + "supportteamid": 1 + }, + "message": "Contact updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -9862,6 +14074,9 @@ "type": "object", "additionalProperties": true, "description": "path: team_id (int), contact_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "name": "First Updated" } } } @@ -9885,7 +14100,46 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "createddate": "2026-01-01T00:00:00.000000Z", + "directpermissions": [], + "email": "", + "failedlogins": 0, + "firstname": "", + "isactive": true, + "islocked": false, + "lastlogindate": "2026-01-01T00:00:00Z", + "lastname": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "mustchangepassword": "", + "permissions": [ + "apitokens.create", + "applications.create", + "applications.delete", + "" + ], + "roles": [ + { + "color": null, + "roleid": 1, + "rolename": "admin" + } + ], + "userid": 1, + "username": "" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -9921,7 +14175,48 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "directpermissions": [ + "endpoints.restart" + ], + "email": "", + "failedlogins": 0, + "firstname": "", + "isactive": true, + "islocked": false, + "lastlogindate": null, + "lastname": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "mustchangepassword": "", + "permissions": [ + "applications.view", + "assets.view", + "backups.download", + "" + ], + "roles": [ + { + "color": "#0DCAF0", + "roleid": 3, + "rolename": "technician" + } + ], + "userid": 2, + "username": "", + "warning": "User created but the welcome email could not be sent." + }, + "message": "User created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -9948,6 +14243,16 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "directpermissions": [ + "endpoints.runscript" + ], + "email": "", + "password": "", + "roles": [], + "sendwelcome": false, + "username": "" } } } @@ -9971,7 +14276,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "message": "User deleted", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -10020,7 +14335,33 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "directpermissions": [], + "email": "", + "failedlogins": 0, + "firstname": "", + "isactive": true, + "islocked": false, + "lastlogindate": "2026-01-01T00:00:00Z", + "lastname": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "mustchangepassword": "", + "permissions": [], + "roles": [], + "userid": 1, + "username": "" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -10069,7 +14410,34 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "directpermissions": [], + "email": "", + "failedlogins": 0, + "firstname": "", + "isactive": true, + "islocked": false, + "lastlogindate": null, + "lastname": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "mustchangepassword": "", + "permissions": [], + "roles": [], + "userid": 2, + "username": "" + }, + "message": "User updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -10109,6 +14477,9 @@ "type": "object", "additionalProperties": true, "description": "path: userid (int); body: JSON (fields not yet documented)" + }, + "example": { + "password": "" } } } @@ -10132,7 +14503,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -10170,7 +14543,45 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "color": null, + "description": "Administrator", + "grantable": null, + "isadmin": true, + "isguest": false, + "permissions": [], + "roleid": 1, + "rolename": "admin", + "usercount": 1 + }, + { + "color": "#6C757D", + "description": "Anyone who is not signed in. Holds nothing by default; grant it a permission to open that up to people with no account.", + "grantable": [ + "applications.view", + "assets.view", + "computers.view", + "" + ], + "isadmin": false, + "isguest": true, + "permissions": [], + "roleid": 2, + "rolename": "guest", + "usercount": 0 + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -10206,7 +14617,24 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "color": "#ff8800", + "description": "colorful", + "permissions": [], + "roleid": 2, + "rolename": "painters" + }, + "message": "Role created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -10233,6 +14661,12 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "color": "#ff8800", + "description": "colorful", + "permissions": [], + "rolename": "painters" } } } @@ -10256,7 +14690,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -10305,7 +14741,26 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "color": "#6C757D", + "description": "Anyone who is not signed in. Holds nothing by default; grant it a permission to open that up to people with no account.", + "permissions": [ + "reports.view" + ], + "roleid": 2, + "rolename": "guest" + }, + "message": "Role updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -10345,6 +14800,11 @@ "type": "object", "additionalProperties": true, "description": "path: roleid (int); body: JSON (fields not yet documented)" + }, + "example": { + "permissions": [ + "reports.view" + ] } } } @@ -10368,7 +14828,38 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "supportemail": "", + "supportphone": "", + "vendor": "Acme", + "vendorid": 1, + "website": null + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -10386,7 +14877,59 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "vendor", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] }, "post": { "tags": [ @@ -10404,7 +14947,29 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "supportemail": "", + "supportphone": "", + "vendor": "Unscoped Vendor", + "vendorid": 1, + "website": null + }, + "message": "Vendor created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -10431,6 +14996,11 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "createddate": "2026-01-01 00:00:00", + "modifieddate": "2026-01-01T00:00:00", + "vendor": "LegacyVendor" } } } @@ -10454,7 +15024,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -10503,7 +15075,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -10552,7 +15126,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -10615,7 +15191,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -10645,6 +15223,14 @@ "schema": { "type": "integer" } + }, + { + "name": "kind", + "in": "query", + "required": false, + "schema": { + "type": "string" + } } ] } @@ -10666,7 +15252,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -10696,6 +15284,14 @@ "schema": { "type": "integer" } + }, + { + "name": "kind", + "in": "query", + "required": false, + "schema": { + "type": "string" + } } ] } @@ -10717,7 +15313,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -10755,7 +15361,51 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "assettypes": [ + "machine" + ], + "displayname": "", + "formats": [ + { + "ext": ".reg", + "hint": "Restore with the Load... button in the NTLARS settings dialog.", + "id": "ntlars", + "label": "NTLARS Load... (.reg)", + "mimetype": "application/octet-stream" + }, + { + "ext": ".reg", + "hint": "Use when importing outside NTLARS on a 64-bit machine.", + "id": "wow6432node", + "label": "Direct reg import (.reg, WOW6432Node)", + "mimetype": "application/octet-stream" + } + ], + "key": "ntlars", + "storagebackend": "shopdb" + }, + { + "assettypes": [ + "machine" + ], + "displayname": "", + "formats": [], + "key": "partmarker", + "storagebackend": "share" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -10793,7 +15443,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -10844,7 +15496,32 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "againstid": 1, + "backuprevisionid": 2, + "changecount": 1, + "changes": [ + { + "after": "OKUMA", + "aftertype": "REG_SZ", + "before": "", + "beforetype": "REG_SZ", + "change": "changed", + "keypath": "General", + "valuename": "Cnc" + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -10874,6 +15551,14 @@ "schema": { "type": "integer" } + }, + { + "name": "against", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } } ] } @@ -10895,7 +15580,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -10925,6 +15612,14 @@ "schema": { "type": "integer" } + }, + { + "name": "format", + "in": "query", + "required": false, + "schema": { + "type": "string" + } } ] } @@ -10946,7 +15641,79 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "accessmethods": [], + "assetid": 1, + "assetnumber": "PC-1", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "computer", + "businessunitid": null, + "computer": { + "accessprotocolnames": [], + "assetid": 1, + "azuredevicecategory": null, + "azuredeviceid": null, + "azureobjectid": null, + "computerid": 1, + "computertypeid": 1, + "computertypename": "Standard", + "createddate": "2026-01-01T00:00:00.000000Z", + "hostname": "", + "isactive": true, + "lastboottime": null, + "lastreporteddate": null, + "loggedinuser": null, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "osid": null, + "vendorid": null + }, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -10964,7 +15731,149 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "assetnumber", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "businessunit_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "businessunitid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "dir", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "asc" + } + }, + { + "name": "location_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "locationid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "os_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "osid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "shopfloor", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "hostname" + } + }, + { + "name": "type_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "typeid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] }, "post": { "tags": [ @@ -10982,7 +15891,71 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "accessmethods": [], + "assetid": 1, + "assetnumber": "AUDIT-PC-1", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "computer", + "businessunitid": null, + "computer": { + "accessprotocolnames": [], + "assetid": 1, + "azuredevicecategory": null, + "azuredeviceid": null, + "azureobjectid": null, + "computerid": 1, + "computertypeid": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "hostname": "", + "isactive": true, + "lastboottime": null, + "lastreporteddate": null, + "loggedinuser": null, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "osid": null, + "vendorid": null + }, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statuscolor": null, + "statusid": 1, + "statusname": "In Use" + }, + "message": "Computer created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -11009,6 +15982,12 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "assetnumber": "PC-IMPORT-1", + "createddate": "2026-01-01 00:00:00", + "hostname": "", + "modifieddate": "2026-01-01T00:00:00" } } } @@ -11032,7 +16011,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -11081,7 +16062,71 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "accessmethods": [], + "assetid": 1, + "assetnumber": "PC-2001", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "computer", + "businessunitid": null, + "communications": [], + "computer": { + "accessprotocolnames": [], + "assetid": 1, + "azuredevicecategory": null, + "azuredeviceid": null, + "azureobjectid": null, + "computerid": 1, + "computertypeid": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "hostname": "", + "imageurl": "/api/models/images/model-1.png", + "isactive": true, + "lastboottime": null, + "lastreporteddate": null, + "loggedinuser": null, + "modelname": "IMG-MODEL-1", + "modelnumberid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "osid": null, + "vendorid": null + }, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -11130,7 +16175,83 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "accessmethods": [ + { + "id": 1, + "link": "rdp://auditpc1", + "name": "RDP", + "port": null, + "portoverride": null, + "protocolid": 1, + "scheme": "rdp" + } + ], + "assetid": 1, + "assetnumber": "AUDIT-PC-1", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "computer", + "businessunitid": null, + "computer": { + "accessprotocolnames": [ + "RDP" + ], + "assetid": 1, + "azuredevicecategory": null, + "azuredeviceid": null, + "azureobjectid": null, + "computerid": 1, + "computertypeid": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "hostname": "", + "isactive": true, + "lastboottime": null, + "lastreporteddate": null, + "loggedinuser": null, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "osid": null, + "vendorid": null + }, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statuscolor": null, + "statusid": 1, + "statusname": "In Use" + }, + "message": "Computer updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -11170,6 +16291,10 @@ "type": "object", "additionalProperties": true, "description": "path: computer_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "gaugelabreference": "GL-2", + "maintenancereference": "MNT-2" } } } @@ -11193,7 +16318,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -11242,7 +16369,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -11305,7 +16434,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -11364,7 +16495,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -11427,7 +16560,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -11478,7 +16613,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -11529,7 +16666,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -11580,7 +16719,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -11618,7 +16759,30 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "computerid": 2, + "hostname": "", + "lastreported": "2026-01-01T00:00:00.000000Z", + "quietdays": 9 + }, + { + "computerid": 1, + "hostname": "", + "lastreported": "2026-01-01T00:00:00.000000Z", + "quietdays": 2 + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -11656,7 +16820,24 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "assetid": 1, + "machinenumber": "2026", + "pccount": 2, + "pcs": "PCONE, PCTWO" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -11694,7 +16875,23 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "byos": [], + "bytype": [], + "nonshopfloor": 0, + "shopfloor": 0, + "total": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -11732,7 +16929,24 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "computerid": 1, + "fqdn": "", + "hostname": "", + "serialnumber": "" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -11770,7 +16984,43 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "action": "service_start", + "description": "Start a Windows service by name on the endpoint.", + "kind": "command", + "label": "Start service", + "mayauthor": true, + "requiredpermission": null + }, + { + "action": "service_stop", + "description": "Stop a running Windows service by name on the endpoint.", + "kind": "command", + "label": "Stop service", + "mayauthor": true, + "requiredpermission": null + }, + { + "action": "service_status", + "description": "Report whether a named Windows service is running.", + "kind": "command", + "label": "Check service", + "mayauthor": true, + "requiredpermission": null + }, + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -11808,7 +17058,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -11844,7 +17096,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -11894,7 +17148,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -11943,7 +17199,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -11992,7 +17250,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -12055,7 +17315,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -12091,7 +17353,42 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "actions": [ + { + "action": "run_script", + "configurationpolicyactionid": 1, + "configurationpolicyid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "desiredstate": null, + "intervalminutes": null, + "isactive": true, + "mode": "gate", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "params": null, + "sortorder": 0 + } + ], + "configurationpolicyid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Legitimate", + "scopejson": null, + "source": "local" + }, + "message": "Configuration policy created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -12118,6 +17415,14 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "actions": [ + { + "action": "run_script" + } + ], + "name": "Legitimate" } } } @@ -12141,7 +17446,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -12190,7 +17497,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -12239,7 +17548,42 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "actions": [ + { + "action": "run_script", + "configurationpolicyactionid": 1, + "configurationpolicyid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "desiredstate": null, + "intervalminutes": null, + "isactive": true, + "mode": "gate", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "params": null, + "sortorder": 0 + } + ], + "configurationpolicyid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "clearer wording", + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "New Name", + "scopejson": null, + "source": "local" + }, + "message": "Configuration policy updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -12279,6 +17623,10 @@ "type": "object", "additionalProperties": true, "description": "path: policy_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "description": "clearer wording", + "name": "New Name" } } } @@ -12302,7 +17650,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -12353,7 +17703,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -12371,7 +17731,18 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + } + ] }, "post": { "tags": [ @@ -12389,7 +17760,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -12439,7 +17812,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -12488,7 +17863,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -12549,7 +17926,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -12612,7 +17991,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -12630,7 +18027,51 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] }, "post": { "tags": [ @@ -12648,7 +18089,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -12698,7 +18141,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -12747,7 +18192,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -12796,7 +18243,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -12859,7 +18308,27 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "First_Name": "Test", + "Last_Name": "Person", + "Picture": null, + "Role": null, + "SSO": 210000001, + "Team": null, + "photofilename": null, + "photourl": null + }, + "message": "Employee photo removed", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -12908,7 +18377,27 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "First_Name": "Test", + "Last_Name": "Person", + "Picture": null, + "Role": null, + "SSO": 210000001, + "Team": null, + "photofilename": "photo-210000001.jpg", + "photourl": "/api/employees/photo/photo-210000001.jpg" + }, + "message": "Employee photo uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -12971,7 +18460,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -13007,7 +18498,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -13057,7 +18550,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -13106,7 +18601,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -13169,7 +18666,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -13219,7 +18718,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -13237,7 +18738,18 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "sso", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "" + } + } + ] } }, "/api/employees/lookup/{sso}": { @@ -13257,7 +18769,26 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "First_Name": "Test", + "Last_Name": "Person", + "Picture": null, + "Role": null, + "SSO": 210000001, + "Team": null, + "photofilename": "photo-210000001.png", + "photourl": "/api/employees/photo/photo-210000001.png" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -13304,7 +18835,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -13350,7 +18883,24 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "error": "No name found in the directory or in a user account", + "mode": "selfhosted", + "name": null, + "photourl": null, + "source": null, + "sso": "999999" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -13401,7 +18951,38 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "First_Name": "Dana", + "Last_Name": "Reyes", + "Picture": null, + "Role": null, + "SSO": 990005, + "Team": null, + "photofilename": null, + "photourl": null + }, + { + "First_Name": "Sam", + "Last_Name": "Reyes", + "Picture": null, + "Role": null, + "SSO": 990006, + "Team": null, + "photofilename": null, + "photourl": null + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -13419,7 +19000,27 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 10 + } + }, + { + "name": "q", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "" + } + } + ] } }, "/api/endpoints": { @@ -13439,7 +19040,58 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "agentversion": null, + "alias": "Bay 3 Controller", + "assetid": 1, + "assetnumber": "fleetbox", + "computerid": 1, + "computertype": null, + "computertypeid": null, + "cpupercent": null, + "createdat": "2026-01-01T00:00:00.000000Z", + "credentialstate": "", + "domain": null, + "endpointstateid": 1, + "health": { + "label": "no agent", + "level": "unknown" + }, + "hostname": "", + "idleseconds": null, + "lastboottime": null, + "lastreportedat": null, + "loggedinuser": null, + "machines": [], + "memorypercent": null, + "mtconnectcheckedat": null, + "mtconnectrunning": null, + "pendingreboot": null, + "status": "never", + "trayversion": null, + "uptimedisplay": null, + "uptimeseconds": null + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -13457,7 +19109,90 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "businessunitid", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "computertypeid", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "credentialstate", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "locationid", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/endpoints/{asset_id}": { @@ -13477,7 +19212,52 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "actionminimums": { + "collect_log": "1.16.0" + }, + "agentcurrent": "1.6.3", + "agentversion": "1.20.0", + "assetid": 2, + "cpupercent": null, + "createdat": "2026-01-01T00:00:00.000000Z", + "credential": { + "credentialprefix": "", + "enrolledvia": "auto", + "issuedat": "2026-01-01T00:00:00.000000Z", + "lastusedat": "2026-01-01T00:00:00.000000Z", + "reenrollblocked": true, + "reenrollblockedreason": "rollback", + "revokedat": "2026-01-01T00:00:00.000000Z", + "state": "reenrollblocked", + "statelabel": "Re-enroll blocked" + }, + "domain": null, + "endpointstateid": 2, + "hostname": "", + "idleseconds": null, + "lastboottime": null, + "lastreportedat": "2026-01-01T00:00:00.000000Z", + "loggedinuser": null, + "memorypercent": null, + "mtconnectcheckedat": null, + "mtconnectrunning": null, + "pendingreboot": null, + "status": "reporting", + "trayversion": null, + "uptimedisplay": null, + "uptimeseconds": null + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -13528,7 +19308,30 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "action": "run_script", + "assetid": 1, + "claimedat": null, + "completedat": null, + "createdat": "2026-01-01T00:00:00.000000Z", + "endpointcommandid": 1, + "requestedby": "", + "resultmessage": null, + "status": "pending", + "target": "script:1", + "targetkind": "script" + }, + "message": "Command queued", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -13568,6 +19371,10 @@ "type": "object", "additionalProperties": true, "description": "path: asset_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "action": "run_script", + "target": "script:1" } } } @@ -13591,7 +19398,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -13640,7 +19449,30 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "action": "collect_log", + "assetid": 1, + "claimedat": null, + "completedat": null, + "createdat": "2026-01-01T00:00:00.000000Z", + "endpointcommandid": 1, + "requestedby": "", + "resultmessage": null, + "status": "pending", + "target": "C:\\logs\\app.log", + "targetkind": "logpath" + }, + "message": "Command queued", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -13680,6 +19512,12 @@ "type": "object", "additionalProperties": true, "description": "path: asset_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "action": "service_status", + "overridereason": "line down, need status", + "policyoverride": true, + "target": "spooler" } } } @@ -13703,7 +19541,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -13733,6 +19573,39 @@ "schema": { "type": "integer" } + }, + { + "name": "eventid", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } } ] } @@ -13754,7 +19627,22 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "action": "service_start", + "held": false + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -13805,7 +19693,16 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -13854,7 +19751,26 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "endpointlockid": 1, + "lockedat": "2026-01-01T00:00:00.000000Z", + "lockedbyuserid": 1, + "lockedbyusername": "", + "reason": "visible to all", + "releasedat": null, + "releasedbyusername": "" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -13903,7 +19819,26 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "endpointlockid": 1, + "lockedat": "2026-01-01T00:00:00.000000Z", + "lockedbyuserid": 1, + "lockedbyusername": "", + "reason": "locked but still reconciled", + "releasedat": null, + "releasedbyusername": "" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -13943,6 +19878,9 @@ "type": "object", "additionalProperties": true, "description": "path: asset_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "reason": "locked but still reconciled" } } } @@ -13966,7 +19904,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -14017,7 +19957,24 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "action": "service_stop", + "createdbyusername": "", + "expiresat": "2026-01-01T00:00:00.000000", + "reason": "vendor on site" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -14066,7 +20023,30 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "action": "service_stop", + "createdbyusername": "", + "expiresat": "2026-01-01T00:00:00.000000", + "reason": "stripping the enclosure" + }, + { + "action": "service_start", + "createdbyusername": "", + "expiresat": "2026-01-01T00:00:00.000000", + "reason": "stripping the enclosure" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -14106,6 +20086,14 @@ "type": "object", "additionalProperties": true, "description": "path: asset_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "actions": [ + "service_start", + "service_stop" + ], + "minutes": 60, + "reason": "stripping the enclosure" } } } @@ -14129,7 +20117,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -14188,7 +20186,64 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "action": "run_script", + "assetid": 1, + "claimedat": null, + "completedat": null, + "createdat": "2026-01-01T00:00:00Z", + "endpointcommandid": 6, + "requestedby": "", + "resultdetail": null, + "resultmessage": null, + "scriptname": "test-open-notepad", + "status": "succeeded", + "target": "script:1", + "targetkind": null + }, + { + "action": "run_script", + "assetid": 1, + "claimedat": null, + "completedat": null, + "createdat": "2026-01-01T00:00:00Z", + "endpointcommandid": 5, + "requestedby": "", + "resultdetail": null, + "resultmessage": null, + "scriptname": "test-open-notepad", + "status": "succeeded", + "target": "script:1", + "targetkind": null + }, + { + "action": "run_script", + "assetid": 1, + "claimedat": null, + "completedat": null, + "createdat": "2026-01-01T00:00:00Z", + "endpointcommandid": 4, + "requestedby": "", + "resultdetail": null, + "resultmessage": null, + "scriptname": "test-open-notepad", + "status": "succeeded", + "target": "script:1", + "targetkind": null + }, + "" + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -14218,6 +20273,15 @@ "schema": { "type": "integer" } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 5 + } } ] } @@ -14239,7 +20303,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -14289,7 +20355,23 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "envkeyfallback": true, + "key": null, + "provisioned": false, + "revoked": 0 + }, + "message": "Endpoint agent key revoked", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -14325,7 +20407,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -14363,7 +20447,39 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "envkeyfallback": false, + "key": { + "createddate": "2026-01-01T00:00:00.000000Z", + "displayprefix": "", + "expiresat": null, + "isactive": true, + "isexpired": false, + "lastusedat": null, + "name": "Endpoint agent key", + "resourcescopes": null, + "scopes": [ + "collector.ingest.endpoints" + ], + "tokenid": 1, + "tokenprefix": "", + "userid": 1 + }, + "provisioned": true, + "secret": "", + "warning": "Save this key now. It will not be shown again. Configure it as the agent X-API-Key." + }, + "message": "Endpoint agent key generated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -14390,7 +20506,8 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" - } + }, + "example": {} } } } @@ -14413,7 +20530,28 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "message": "Scheduled maintenance at 3:30 PM", + "notificationId": 1, + "notificationid": 1, + "timeout": 0, + "title": "Scheduled maintenance at 3:30 PM", + "type": "Fleet Alert", + "typeColor": "#ffc107", + "typecolor": "#ffc107" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -14431,7 +20569,17 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "hostname", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/endpoints/alerts/{notification_id}/acknowledge": { @@ -14451,7 +20599,20 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "acknowledged": true + }, + "message": "Alert already acknowledged", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -14491,6 +20652,10 @@ "type": "object", "additionalProperties": true, "description": "path: notification_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "assetid": 1, + "hostname": "" } } } @@ -14514,7 +20679,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -14565,7 +20732,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -14616,7 +20785,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -14676,7 +20847,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -14727,7 +20900,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -14778,7 +20953,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -14829,7 +21006,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -14880,7 +21059,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -14931,7 +21112,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -14982,7 +21165,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -15033,7 +21218,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -15084,7 +21271,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -15135,7 +21324,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -15186,7 +21377,33 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "action": { + "running": false, + "start": "process_start", + "status": "process_status", + "stop": "process_stop", + "target": "dncMain.exe", + "targetkind": "process" + }, + "drift": true, + "kind": "process", + "name": "eDNC", + "statecolor": "gray", + "statelabel": "Unknown" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -15237,7 +21454,51 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "agentbehind": false, + "agentcurrent": "1.6.3", + "agentversion": "1.20.0", + "credential": { + "credentialprefix": "", + "enrolledvia": "auto", + "issuedat": "2026-01-01T00:00:00.000000Z", + "lastusedat": "2026-01-01T00:00:00.000000Z", + "reenrollblocked": true, + "reenrollblockedreason": "rollback", + "revokedat": "2026-01-01T00:00:00.000000Z", + "state": "reenrollblocked", + "statelabel": "Re-enroll blocked" + }, + "fields": [ + { + "label": "Agent", + "mono": true, + "value": "1.20.0" + }, + { + "format": "datetime", + "label": "Last reported", + "mono": false, + "value": "2026-01-01T00:00:00.000000Z" + }, + { + "label": "Credential", + "mono": false, + "value": "Re-enroll blocked" + }, + "" + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -15288,7 +21549,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -15339,7 +21602,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -15390,7 +21655,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -15441,7 +21708,24 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "action": "created", + "assetid": 1, + "identityvalue": "BAY-01", + "status": "ok", + "warnings": [] + }, + "message": "endpoints collector created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -15468,6 +21752,10 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "agentversion": "1.20.0", + "hostname": "" } } } @@ -15491,7 +21779,30 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "action": "mtconnect_status", + "assetid": 1, + "claimedat": null, + "completedat": "2026-01-01T00:00:00.000000Z", + "createdat": "2026-01-01T00:00:00.000000Z", + "endpointcommandid": 1, + "requestedby": "", + "resultmessage": null, + "status": "succeeded", + "target": null, + "targetkind": null + }, + "message": "Result recorded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -15521,6 +21832,14 @@ "schema": { "type": "integer" } + }, + { + "name": "hostname", + "in": "query", + "required": false, + "schema": { + "type": "string" + } } ], "requestBody": { @@ -15531,6 +21850,9 @@ "type": "object", "additionalProperties": true, "description": "path: command_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "succeeded": true } } } @@ -15554,7 +21876,22 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "action": "mtconnect_status", + "endpointcommandid": 1, + "target": null, + "targetkind": null + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -15572,7 +21909,17 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "hostname", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/endpoints/dashboard/enrollmentpending": { @@ -15592,7 +21939,22 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "pendingcount": 2, + "summary": "2 PCs waiting for enrollment approval" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -15630,7 +21992,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -15668,7 +22032,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "assetid": 2, + "blockeddays": 9, + "computerid": 2, + "hostname": "", + "reason": "rollback" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -15706,7 +22088,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -15744,7 +22128,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -15778,7 +22164,22 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "credential": "", + "status": "enrolled" + }, + "message": "Endpoint enrolled", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -15799,6 +22200,13 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "agentversion": "1.20.0", + "deviceid": null, + "enrollnonce": "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm", + "hostname": "", + "serialnumber": "" } } } @@ -15822,7 +22230,108 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "computers": [ + { + "assetid": 1, + "credentialprefix": "", + "enrolledvia": "auto", + "hostname": "", + "issuedat": "2026-01-01T00:00:00.000000Z", + "lastusedat": null, + "reenrollblocked": false, + "reenrollblockedreason": null, + "serialnumber": "", + "state": "enrolled" + }, + { + "assetid": 2, + "credentialprefix": "", + "enrolledvia": null, + "hostname": "", + "issuedat": null, + "lastusedat": null, + "reenrollblocked": false, + "reenrollblockedreason": null, + "serialnumber": "", + "state": "notenrolled" + }, + { + "assetid": 3, + "credentialprefix": "", + "enrolledvia": null, + "hostname": "", + "issuedat": null, + "lastusedat": null, + "reenrollblocked": false, + "reenrollblockedreason": null, + "serialnumber": "", + "state": "pending" + } + ], + "counts": { + "enrolled": 1, + "notenrolled": 1, + "pending": 1, + "pendingcomputers": 1 + }, + "requests": [ + { + "agentversion": "1.20.0", + "decidedat": null, + "decidedby": null, + "deviceid": null, + "hostname": "", + "lastseenat": "2026-01-01T00:00:00.000000Z", + "matchedassetid": 3, + "reason": "no serial on record", + "requestedat": "2026-01-01T00:00:00.000000Z", + "requestid": "00000000", + "serialnumber": "", + "sourceip": "127.0.0.1", + "status": "pending" + }, + { + "agentversion": "1.20.0", + "decidedat": null, + "decidedby": null, + "deviceid": null, + "hostname": "", + "lastseenat": "2026-01-01T00:00:00.000000Z", + "matchedassetid": 1, + "reason": null, + "requestedat": "2026-01-01T00:00:00.000000Z", + "requestid": "00000000", + "serialnumber": "", + "sourceip": "127.0.0.1", + "status": "issued" + } + ], + "settings": { + "autoenroll": true, + "maxpendingenrollments": 500, + "revokeddeniessharedkey": false, + "sharedkeyallowed": true + } + }, + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 3, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -15840,7 +22349,25 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "state", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/endpoints/enrollment/assets/{assetid}/allow-reenroll": { @@ -15860,7 +22387,29 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "credentialid": 1, + "credentialprefix": "", + "enrolledvia": "auto", + "issuedat": "2026-01-01T00:00:00.000000Z", + "lastusedat": null, + "reenrollblocked": false, + "reenrollblockedreason": null, + "revokedat": "2026-01-01T00:00:00.000000Z", + "revokedby": "testadmin" + }, + "message": "Re-enrollment allowed", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -15923,7 +22472,29 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "credentialid": 1, + "credentialprefix": "", + "enrolledvia": "auto", + "issuedat": "2026-01-01T00:00:00.000000Z", + "lastusedat": null, + "reenrollblocked": true, + "reenrollblockedreason": "rollback", + "revokedat": "2026-01-01T00:00:00.000000Z", + "revokedby": "testadmin" + }, + "message": "Endpoint credential revoked", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -15963,6 +22534,10 @@ "type": "object", "additionalProperties": true, "description": "path: assetid (int); body: JSON (fields not yet documented)" + }, + "example": { + "blockreenroll": true, + "reason": "rollback" } } } @@ -15986,7 +22561,85 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "computers": [ + { + "agentversion": "1.19.0", + "assetid": 2, + "blockers": [], + "hostname": "", + "lastreportedat": "2026-01-01T00:00:00.000000Z", + "state": "ready" + }, + { + "agentversion": "1.19.0", + "assetid": 3, + "blockers": [], + "hostname": "", + "lastreportedat": "2026-01-01T00:00:00.000000Z", + "state": "ready" + }, + { + "agentversion": "1.20.0", + "assetid": 1, + "blockers": [ + "awaiting enrollment" + ], + "hostname": "", + "lastreportedat": "2026-01-01T00:00:00.000000Z", + "state": "blocked" + } + ], + "counts": { + "blocked": 1, + "enrolled": 0, + "notreporting": 0, + "pending": 0, + "ready": 2, + "upgrading": 0 + }, + "target": "1.20.0", + "waves": [ + { + "createdat": "2026-01-01T00:00:00Z", + "createdby": "", + "done": false, + "enrolled": 0, + "failed": 1, + "queued": 3, + "size": 3, + "waveid": 1 + }, + { + "createdat": "2026-01-01T00:00:00Z", + "createdby": "", + "done": false, + "enrolled": 0, + "failed": 0, + "queued": 2, + "size": 2, + "waveid": 2 + } + ] + }, + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 3, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -16004,7 +22657,25 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "state", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/endpoints/enrollment/migration/wave": { @@ -16024,7 +22695,38 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "queued": [ + 1 + ], + "retried": [], + "skipped": [ + { + "assetid": 2, + "reason": "offline" + }, + { + "assetid": 3, + "reason": "notreporting" + }, + { + "assetid": 999999, + "reason": "not a computer" + } + ], + "waveid": 1 + }, + "message": "1 PC(s) queued", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -16051,6 +22753,10 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "action": "run_script", + "target": "script:1" } } } @@ -16074,7 +22780,33 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "agentversion": "1.20.0", + "decidedat": "2026-01-01T00:00:00.000000Z", + "decidedby": "testadmin", + "deviceid": null, + "hostname": "", + "hostnameresult": "", + "lastseenat": "2026-01-01T00:00:00.000000Z", + "matchedassetid": 1, + "reason": null, + "requestedat": "2026-01-01T00:00:00.000000Z", + "requestid": "00000000", + "serialnumber": "", + "sourceip": "127.0.0.1", + "status": "approved" + }, + "message": "Enroll request approved; that computer was renamed to the hostname the PC reports", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -16114,6 +22846,10 @@ "type": "object", "additionalProperties": true, "description": "path: requestid (int); body: JSON (fields not yet documented)" + }, + "example": { + "assetid": 1, + "sethostname": "" } } } @@ -16137,7 +22873,32 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "agentversion": "1.20.0", + "decidedat": "2026-01-01T00:00:00.000000Z", + "decidedby": "testadmin", + "deviceid": null, + "hostname": "", + "lastseenat": "2026-01-01T00:00:00.000000Z", + "matchedassetid": 1, + "reason": "awaiting approval", + "requestedat": "2026-01-01T00:00:00.000000Z", + "requestid": "00000000", + "serialnumber": "", + "sourceip": "127.0.0.1", + "status": "rejected" + }, + "message": "Enroll request rejected", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -16200,7 +22961,23 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "autoenroll": true, + "maxpendingenrollments": 500, + "revokeddeniessharedkey": false, + "sharedkeyallowed": false + }, + "message": "Enrollment settings saved", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -16227,6 +23004,9 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "revokeddeniessharedkey": true } } } @@ -16250,7 +23030,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -16301,7 +23083,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -16352,7 +23136,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -16388,7 +23174,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -16438,7 +23226,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -16487,7 +23277,30 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "createdat": "2026-01-01T00:00:00.000000Z", + "datacollectorpath": null, + "displayname": "", + "endpointmonitorid": 1, + "isactive": false, + "kind": "process", + "launchpath": "C:\\Program Files\\Dnc\\bin\\DncMain.exe", + "logcollectorpath": null, + "processname": "UDC", + "servicename": null, + "source": "shipped" + }, + "message": "Monitor updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -16527,6 +23340,9 @@ "type": "object", "additionalProperties": true, "description": "path: monitor_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "launchpath": "D:\\Dnc\\bin\\DncMain.exe" } } } @@ -16550,7 +23366,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -16568,7 +23386,17 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "pctype", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/endpoints/monitors/resolved": { @@ -16588,7 +23416,26 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "processes": [ + { + "app": "Edge", + "launchpath": "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe", + "name": "msedge.exe" + } + ], + "services": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -16606,7 +23453,17 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "hostname", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/endpoints/operatorreports": { @@ -16626,7 +23483,28 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "createdat": "2026-01-01T00:00:00.000000Z", + "details": "the CMM app will not start", + "hasimage": true, + "imagebytesize": 70, + "imagemimetype": "image/png", + "operatorreportid": 1, + "reason": "Other (free text)", + "reportedhostname": "" + }, + "message": "Operator report received", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -16676,7 +23554,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -16727,7 +23607,42 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "reports": [ + { + "assetid": 1, + "createdat": "2026-01-01T00:00:00.000000Z", + "details": "second", + "hasimage": false, + "imagebytesize": null, + "imagemimetype": null, + "operatorreportid": 2, + "reason": "Other (free text)", + "reportedhostname": "" + }, + { + "assetid": 1, + "createdat": "2026-01-01T00:00:00.000000Z", + "details": "first", + "hasimage": false, + "imagebytesize": null, + "imagemimetype": null, + "operatorreportid": 1, + "reason": "Other (free text)", + "reportedhostname": "" + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -16778,7 +23693,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -16829,7 +23746,28 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "expires_in_days": 29, + "is_self_signed": true, + "issuer": "CN = pin.example.test", + "not_after": "Oct 18 18:57:47 2026 GMT", + "not_before": "Sep 17 18:57:47 2026 GMT", + "san_list": [], + "sha1thumbprint": "", + "spkisha256": "", + "subject_cn": "pin.example.test", + "thumbprint": "99:F1:E1:1D:5A:66:EE:44:8C:CA:5C:D8:BF:EA:C3:F3:8F:82:40:D3:5E:C9:4D:F0:AC:0B:3C:AD:80:D7:C1:DF" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -16867,7 +23805,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -16905,7 +23845,19 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "message": "Certificate generated successfully. Nginx is restarting." + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -16932,6 +23884,11 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "cn": "shopdb.example.test", + "days": 30, + "san": "" } } } @@ -16955,7 +23912,24 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "configured": true, + "error": "Could not read the certificate from shopdb.example.test:443 (Connection refused)", + "matchesfile": false, + "presentedsha1thumbprint": null, + "presentedspkisha256": null, + "url": "https://shopdb.example.test" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -16993,7 +23967,19 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "message": "Certificate uploaded successfully. Nginx is restarting." + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -17043,7 +24029,29 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "agentcurrent": null, + "agentlegacy": null, + "agentstaged": false, + "agentversions": [], + "certavailable": true, + "serverpin": "", + "sharedkeyallowed": false, + "traycurrent": null, + "traylegacy": null, + "traystaged": false, + "trayversions": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -17081,7 +24089,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -17130,7 +24140,51 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "binary": "agent", + "current": "", + "registration": { + "agentversion": "", + "appid": 1, + "appname": "ShopDB Endpoint Agent", + "contents": { + "agent": { + "filename": "shopdbagent.exe", + "sha256": "", + "version": "" + } + }, + "created": false, + "installpath": "/api/applications/package/1/shopdb-endpoint-agent.zip", + "ok": true, + "sha256": "", + "size": 135 + }, + "versions": [ + { + "iscurrent": true, + "size": 130, + "uploadedat": "2026-01-01T00:00:00.000000Z", + "version": "" + }, + { + "iscurrent": false, + "size": 130, + "uploadedat": "2026-01-01T00:00:00.000000Z", + "version": "" + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -17193,7 +24247,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -17256,7 +24312,51 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "binary": "agent", + "current": "", + "registration": { + "agentversion": "", + "appid": 1, + "appname": "ShopDB Endpoint Agent", + "contents": { + "agent": { + "filename": "shopdbagent.exe", + "sha256": "", + "version": "" + } + }, + "created": false, + "installpath": "/api/applications/package/1/shopdb-endpoint-agent.zip", + "ok": true, + "sha256": "", + "size": 135 + }, + "versions": [ + { + "iscurrent": false, + "size": 258, + "uploadedat": "2026-01-01T00:00:00.000000Z", + "version": "" + }, + { + "iscurrent": true, + "size": 258, + "uploadedat": "2026-01-01T00:00:00.000000Z", + "version": "" + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -17296,6 +24396,9 @@ "type": "object", "additionalProperties": true, "description": "path: binkey (string); body: JSON (fields not yet documented)" + }, + "example": { + "version": "" } } } @@ -17319,7 +24422,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -17382,7 +24487,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -17420,7 +24527,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -17458,7 +24567,34 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "tokens": [ + { + "createdat": "2026-01-01T00:00:00.000000Z", + "createdby": "", + "expiresat": "2026-01-01T00:00:00.000000Z", + "lastusedat": null, + "lastusedip": null, + "maxuses": 25, + "remaining": 25, + "revokedat": null, + "state": "active", + "tokenid": 1, + "tokenprefix": "", + "usecount": 0 + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -17494,7 +24630,36 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "command": "powershell -ExecutionPolicy Bypass -c \"try { New-Item -ItemType Directory -Force -Path 'C:\\Program Files\\ShopDB\\bootstrap' | Out-Null; icacls 'C:\\Program Files\\ShopDB\\bootstrap' /inheritance:r /grant ...", + "scripturl": "https://shopdb.test/shopdb/api/endpoints/siteagent/bootstrap.ps1?t=&via=download", + "secret": "", + "token": { + "createdat": "2026-01-01T00:00:00.000000Z", + "createdby": "", + "expiresat": "2026-01-01T00:00:00.000000Z", + "lastusedat": null, + "lastusedip": null, + "maxuses": 500, + "remaining": 500, + "revokedat": null, + "state": "active", + "tokenid": 1, + "tokenprefix": "", + "usecount": 0 + } + }, + "message": "Bootstrap token created. The command is shown once.", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -17521,6 +24686,10 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "hours": 6, + "maxuses": 3 } } } @@ -17544,7 +24713,33 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "token": { + "createdat": "2026-01-01T00:00:00.000000Z", + "createdby": "", + "expiresat": "2026-01-01T00:00:00.000000Z", + "lastusedat": null, + "lastusedip": null, + "maxuses": 25, + "remaining": 25, + "revokedat": "2026-01-01T00:00:00.000000Z", + "state": "revoked", + "tokenid": 1, + "tokenprefix": "", + "usecount": 0 + } + }, + "message": "Bootstrap token revoked", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -17607,7 +24802,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -17634,6 +24831,13 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "baseurl": "https://shopdb.test", + "bundlecert": true, + "cadenceseconds": 900, + "collectorkey": "", + "nextserverpin": "" } } } @@ -17657,7 +24861,22 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "appid": 1, + "appname": "eDNC (tracked)" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -17695,7 +24914,20 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "allowedcidrs": "", + "shareroot": "" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -17731,7 +24963,19 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "shareroot": "" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -17758,6 +25002,9 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "shareroot": "" } } } @@ -17781,7 +25028,27 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "computerid": null, + "entryname": "Install OpenText", + "exitcode": 1603, + "hostname": "", + "message": "Fatal error during installation", + "receivedat": "2026-01-01T00:00:00Z", + "scopename": "gea-shopfloor-cmm" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -17819,7 +25086,19 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "deleted": 2 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -17868,7 +25147,42 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "InUseCheck": { + "Behavior": "CloseAndReopen", + "Processes": [ + { + "ExePath": "C:\\PCDLRN.exe", + "GracefulCloseTimeoutSec": 15, + "Name": "PCDLRN" + } + ] + }, + "Installer": "apps/pcdmis.msi", + "LogFile": "C:\\Logs\\pcdmis.log", + "Name": "PC-DMIS", + "PCTypesStrict": true, + "PreEnrollment": true, + "Type": "MSI", + "appid": null, + "appname": null, + "entryid": 1, + "haspayload": false, + "payloadref": null, + "payloadsha256": null, + "payloadsource": "smb", + "sortorder": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -17908,6 +25222,24 @@ "type": "object", "additionalProperties": true, "description": "path: entryid (int); body: JSON (fields not yet documented)" + }, + "example": { + "InUseCheck": { + "Behavior": "CloseAndReopen", + "Processes": [ + { + "ExePath": "C:\\PCDLRN.exe", + "GracefulCloseTimeoutSec": 15, + "Name": "PCDLRN" + } + ] + }, + "Installer": "apps/pcdmis.msi", + "LogFile": "C:\\Logs\\pcdmis.log", + "Name": "PC-DMIS", + "PCTypesStrict": true, + "PreEnrollment": true, + "Type": "MSI" } } } @@ -17931,7 +25263,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -17980,7 +25314,32 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "Name": "eDNC install", + "PayloadRef": "config.reg", + "PayloadSha256": "", + "PayloadSource": "inline", + "Script": "scripts/x.ps1", + "Type": "PS1", + "appid": null, + "appname": null, + "entryid": 1, + "haspayload": true, + "payloadref": "config.reg", + "payloadsha256": "", + "payloadsource": "inline", + "sortorder": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -18040,10 +25399,32 @@ ], "responses": { "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "description": "Success. This route does NOT return the standard envelope; the example is a transcript of a real response.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "example": { + "Version": "2.6", + "Applications": [ + { + "Name": "Alpha", + "Type": "MSI", + "Installer": "apps/alpha.msi", + "DetectionMethod": "Registry", + "DetectionPath": "HKLM:\\SOFTWARE\\Alpha" + }, + { + "Name": "Beta", + "Type": "PS1", + "Script": "scripts/beta.ps1", + "DetectionMethod": "Always" + }, + { + "Name": "Gamma", + "Type": "PS1", + "Script": "scripts/gamma.ps1" + } + ] + } } } }, @@ -18061,7 +25442,25 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "pctype", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "phase", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/geenforce/payload/{sha256}": { @@ -18081,7 +25480,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -18132,7 +25533,21 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "reportid": 1, + "status": "selfhealed" + }, + "message": "recorded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -18159,6 +25574,42 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "appliedversion": 1, + "counts": { + "failed": 1, + "filtered": 0, + "installed": 2, + "skipped": 1 + }, + "enforcerversion": "2.6", + "hostname": "", + "phase": "runtime", + "results": [ + { + "action": "installed", + "exitcode": 0, + "message": "rule re-added", + "name": "VNC firewall rule", + "selfhealed": true + }, + { + "action": "skipped", + "exitcode": 0, + "message": "", + "name": "PC-DMIS 2023.1", + "selfhealed": false + }, + { + "action": "installed", + "exitcode": 0, + "name": "eDNC config", + "selfhealed": true + }, + "" + ], + "scopename": "gea-shopfloor-cmm" } } } @@ -18182,7 +25633,55 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "appliedversion": null, + "assetnumber": "WJBAK10", + "backupcollectedat": "2026-01-01T00:00:00.000000Z", + "backupkind": "partmarker", + "backuplastseen": "2026-01-01T00:00:00.000000Z", + "backupok": false, + "backupstaleafterdays": 3, + "computerid": 1, + "displayrole": null, + "enforcerversion": null, + "failed": 0, + "filtered": 0, + "hostname": "", + "installed": 0, + "isstale": false, + "lastcheckin": null, + "latestversion": 1, + "levelid": null, + "location": null, + "machineassetid": null, + "machinenumber": null, + "machinepluginid": null, + "mapx": null, + "mapy": null, + "phase": "runtime", + "receivedat": "2026-01-01T00:00:00.000000Z", + "receivedlatest": false, + "reportid": 1, + "scopename": "gea-shopfloor-cmm", + "skipped": 0, + "staleafterminutes": 30, + "status": "ok", + "toolassetid": null, + "toolassetnumber": null, + "toolpluginid": null + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -18200,7 +25699,25 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "hostname", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "scopename", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/geenforce/reports/{reportid}": { @@ -18220,7 +25737,50 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "appliedversion": 1, + "hostname": "", + "latestversion": 1, + "phase": "runtime", + "receivedlatest": true, + "reportid": 1, + "results": [ + { + "action": "installed", + "entryname": "VNC firewall rule", + "exitcode": 0, + "message": "rule re-added", + "selfhealed": true + }, + { + "action": "skipped", + "entryname": "PC-DMIS 2023.1", + "exitcode": 0, + "message": "", + "selfhealed": false + }, + { + "action": "installed", + "entryname": "eDNC config", + "exitcode": 0, + "message": null, + "selfhealed": true + }, + "" + ], + "scopename": "gea-shopfloor-cmm", + "status": "failed" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -18271,7 +25831,30 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "computertypeid": null, + "entrycount": 2, + "iscommon": false, + "manifestversion": "2.6", + "measuringtooltypeid": null, + "minlibversion": null, + "phase": "runtime", + "publishedversion": 1, + "scopeid": 1, + "scopename": "gea-shopfloor-cmm" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -18307,7 +25890,28 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "computertypeid": null, + "description": "test scope", + "entrycount": 0, + "iscommon": false, + "manifestversion": "1.0", + "measuringtooltypeid": null, + "phase": "runtime", + "publishedversion": null, + "scopeid": 1, + "scopename": "gea-shopfloor-collections" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -18334,6 +25938,11 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "description": "test scope", + "phase": "runtime", + "scopename": "gea-shopfloor-collections" } } } @@ -18357,7 +25966,19 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "deleted": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -18406,7 +26027,59 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "computertypeid": null, + "description": "test scope", + "entries": [ + { + "Name": "Beta", + "Script": "scripts/b.ps1", + "Type": "PS1", + "appid": null, + "appname": null, + "entryid": 2, + "haspayload": false, + "payloadref": null, + "payloadsha256": null, + "payloadsource": "smb", + "sortorder": 0 + }, + { + "DetectionMethod": "FileVersion", + "DetectionPath": "C:\\a.exe", + "DetectionValue": "1.0", + "Installer": "apps/a.exe", + "Name": "Alpha", + "Type": "EXE", + "appid": null, + "appname": null, + "entryid": 1, + "haspayload": false, + "payloadref": null, + "payloadsha256": null, + "payloadsource": "smb", + "sortorder": 1 + } + ], + "entrycount": 2, + "iscommon": false, + "manifestversion": "1.0", + "measuringtooltypeid": null, + "phase": "runtime", + "publishedversion": null, + "scopeid": 1, + "scopename": "gea-shopfloor-test" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -18455,7 +26128,28 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "computertypeid": 3, + "description": "updated", + "entrycount": 0, + "iscommon": false, + "manifestversion": "1.0", + "measuringtooltypeid": null, + "phase": "runtime", + "publishedversion": null, + "scopeid": 1, + "scopename": "gea-shopfloor-test" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -18495,6 +26189,10 @@ "type": "object", "additionalProperties": true, "description": "path: scopeid (int); body: JSON (fields not yet documented)" + }, + "example": { + "computertypeid": 3, + "description": "updated" } } } @@ -18518,7 +26216,34 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "computersplugin": false, + "phase": "runtime", + "rows": [ + { + "appid": 1, + "appname": "PC-DMIS", + "coveragenote": "computers plugin not installed; install counts unavailable", + "entryid": 1, + "entryname": "PC-DMIS 2019", + "expectedversion": "2019 R1", + "installedcount": null, + "versionmatchcount": null + } + ], + "scopeid": 1, + "scopename": "gea-shopfloor-cmm" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -18569,7 +26294,42 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "InUseCheck": { + "Behavior": "CloseAndReopen", + "Processes": [ + { + "ExePath": "C:\\PCDLRN.exe", + "GracefulCloseTimeoutSec": 15, + "Name": "PCDLRN" + } + ] + }, + "Installer": "apps/pcdmis.msi", + "LogFile": "C:\\Logs\\pcdmis.log", + "Name": "PC-DMIS", + "PCTypesStrict": true, + "PreEnrollment": true, + "Type": "MSI", + "appid": null, + "appname": null, + "entryid": 1, + "haspayload": false, + "payloadref": null, + "payloadsha256": null, + "payloadsource": "smb", + "sortorder": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -18609,6 +26369,24 @@ "type": "object", "additionalProperties": true, "description": "path: scopeid (int); body: JSON (fields not yet documented)" + }, + "example": { + "InUseCheck": { + "Behavior": "CloseAndReopen", + "Processes": [ + { + "ExePath": "C:\\PCDLRN.exe", + "GracefulCloseTimeoutSec": 15, + "Name": "PCDLRN" + } + ] + }, + "Installer": "apps/pcdmis.msi", + "LogFile": "C:\\Logs\\pcdmis.log", + "Name": "PC-DMIS", + "PCTypesStrict": true, + "PreEnrollment": true, + "Type": "MSI" } } } @@ -18632,7 +26410,22 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "order": [ + 2, + 1 + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -18672,6 +26465,12 @@ "type": "object", "additionalProperties": true, "description": "path: scopeid (int); body: JSON (fields not yet documented)" + }, + "example": { + "order": [ + 2, + 1 + ] } } } @@ -18695,7 +26494,19 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "path": "" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -18758,7 +26569,37 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "manifest": { + "Applications": [ + { + "DetectionMethod": "Registry", + "DetectionPath": "HKLM:\\SOFTWARE\\Alpha", + "Installer": "apps/alpha.msi", + "Name": "Alpha", + "Type": "MSI" + }, + { + "DetectionMethod": "Always", + "Name": "Beta", + "Script": "scripts/beta.ps1", + "Type": "PS1" + } + ], + "Version": "2.6" + }, + "scopename": "gea-shopfloor-cmm" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -18809,7 +26650,19 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "versionnumber": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -18849,6 +26702,9 @@ "type": "object", "additionalProperties": true, "description": "path: scopeid (int); body: JSON (fields not yet documented)" + }, + "example": { + "notes": "v1" } } } @@ -18872,7 +26728,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -18923,7 +26781,19 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "versionnumber": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -18963,6 +26833,9 @@ "type": "object", "additionalProperties": true, "description": "path: scopeid (int); body: JSON (fields not yet documented)" + }, + "example": { + "versionnumber": 1 } } } @@ -18986,7 +26859,38 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "applied": [ + "CLM (untagged)", + "PC-DMIS 2019" + ], + "filtered": [ + { + "filteredby": [ + "_CmmVersion" + ], + "name": "PC-DMIS 2016" + } + ], + "profile": { + "cmmversion": "2019", + "hostname": "", + "machinenumber": null, + "pctype": "gea-shopfloor-cmm", + "phase": "runtime", + "subtype": null + } + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -19016,6 +26920,46 @@ "schema": { "type": "integer" } + }, + { + "name": "cmmversion", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "hostname", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "machinenumber", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "pctype", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "subtype", + "in": "query", + "required": false, + "schema": { + "type": "string" + } } ] } @@ -19037,7 +26981,32 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "iscurrent": false, + "notes": "v2", + "publishedat": "2026-01-01T00:00:00.000000Z", + "publishedby": 1, + "versionnumber": 2 + }, + { + "iscurrent": true, + "notes": "v1", + "publishedat": "2026-01-01T00:00:00.000000Z", + "publishedby": 1, + "versionnumber": 1 + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -19088,7 +27057,32 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "manifest": { + "Applications": [ + { + "Name": "Alpha", + "Type": "MSI" + }, + { + "Name": "Beta", + "Type": "PS1" + } + ], + "Version": "1.0" + }, + "versionnumber": 2 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -19147,7 +27141,33 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "canedit": false, + "checklistitems": 0, + "createdat": "2026-01-01T00:00:00.000000Z", + "guideid": 1, + "ispublished": true, + "publishedat": "2026-01-01T00:00:00.000000Z", + "revision": 2, + "slug": "published-procedure", + "status": "published", + "summary": null, + "tags": [], + "title": "Published procedure", + "updatedat": "2026-01-01T00:00:00.000000Z" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -19165,7 +27185,25 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "tag", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] }, "post": { "tags": [ @@ -19183,7 +27221,30 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "blocks": [], + "createdat": "2026-01-01T00:00:00.000000Z", + "guideid": 1, + "publishedat": null, + "revision": 1, + "slug": "replacing-a-marker-controler", + "status": "draft", + "summary": null, + "tags": [], + "title": "Replacing a marker controler", + "updatedat": "2026-01-01T00:00:00.000000Z" + }, + "message": "Guide created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -19210,6 +27271,10 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "blocks": [], + "title": "Replacing a marker controler" } } } @@ -19233,7 +27298,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -19282,7 +27349,31 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "blocks": [], + "createdat": "2026-01-01T00:00:00.000000Z", + "guideid": 1, + "publishedat": null, + "revision": 2, + "saved": true, + "slug": "replacing-a-marker-controler", + "status": "draft", + "summary": null, + "tags": [], + "title": "Replacing a marker controller", + "updatedat": "2026-01-01T00:00:00.000000Z" + }, + "message": "Saved", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -19322,6 +27413,10 @@ "type": "object", "additionalProperties": true, "description": "path: guideid (int); body: JSON (fields not yet documented)" + }, + "example": { + "blocks": [], + "title": "Replacing a marker controller" } } } @@ -19345,7 +27440,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -19408,7 +27505,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -19479,7 +27578,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -19530,7 +27631,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -19593,7 +27696,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -19644,7 +27749,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -19695,7 +27802,26 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "bytesize": 40, + "createdat": "2026-01-01T00:00:00.000000Z", + "filename": "screenshot.png", + "guideid": null, + "guideimageid": 1, + "mimetype": "image/png", + "url": "/api/guides/images/1" + }, + "message": "Image uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -19745,7 +27871,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -19796,7 +27924,35 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "blocks": [ + { + "id": "b1", + "text": "secret draft wording", + "type": "paragraph" + } + ], + "createdat": "2026-01-01T00:00:00.000000Z", + "createdby": "", + "guideid": 1, + "guiderevisionid": 1, + "note": null, + "revision": 1, + "shorthash": "", + "summary": null, + "tags": [], + "title": "Published procedure" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -19847,7 +28003,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -19885,7 +28051,74 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "appid": 1, + "application": { + "appid": 1, + "appname": "Spotfire" + }, + "clicks": 0, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "keywords": "install", + "lastupdated": "2026-01-01T00:00:00.000000Z", + "linkid": 3, + "linkurl": "https://kb.example/c", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "shortdescription": "Spotfire install guide" + }, + { + "appid": 1, + "application": { + "appid": 1, + "appname": "Spotfire" + }, + "clicks": 0, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "keywords": "export", + "lastupdated": "2026-01-01T00:00:00.000000Z", + "linkid": 2, + "linkurl": "https://kb.example/b", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "shortdescription": "Export a visualization" + }, + { + "appid": 1, + "application": { + "appid": 1, + "appname": "Spotfire" + }, + "clicks": 0, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "keywords": "login", + "lastupdated": "2026-01-01T00:00:00.000000Z", + "linkid": 1, + "linkurl": "https://kb.example/a", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "shortdescription": "Dashboard login steps" + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 3, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -19903,7 +28136,84 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "appid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "linkurl", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "order", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "desc" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "shortdescription", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "clicks" + } + } + ] }, "post": { "tags": [ @@ -19921,7 +28231,29 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "appid": null, + "clicks": 0, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "keywords": "printer reset", + "lastupdated": "2026-01-01T00:00:00.000000Z", + "linkid": 1, + "linkurl": "https://kb.example/printer-reset", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "shortdescription": "How to reset a printer" + }, + "message": "Article created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -19948,6 +28280,11 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "keywords": "printer reset", + "linkurl": "https://kb.example/printer-reset", + "shortdescription": "How to reset a printer" } } } @@ -19971,7 +28308,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -20020,7 +28359,29 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "appid": null, + "application": null, + "clicks": 0, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "keywords": "printer reset", + "lastupdated": "2026-01-01T00:00:00.000000Z", + "linkid": 1, + "linkurl": "https://kb.example/printer-reset", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "shortdescription": "How to reset a printer" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -20069,7 +28430,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -20132,7 +28495,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -20195,7 +28560,20 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "totalarticles": 0, + "totalclicks": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -20233,7 +28611,78 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "assetid": 1, + "assetnumber": "0410", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "machine", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "dualpathpartner": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "machine": { + "assetid": 1, + "controllermodelid": null, + "controllervendorid": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "islocationonly": false, + "lastmaintenancedate": null, + "machineid": 1, + "machinetypeid": 1, + "machinetypename": "Part Washer", + "maintenanceintervaldays": null, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextmaintenancedate": null, + "requiresmanualconfig": false, + "vendorid": 1, + "vendorname": "Acme Industrial" + }, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Cell 4 washer", + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -20251,7 +28700,141 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "assetnumber", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "businessunit_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "businessunitid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "dir", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "asc" + } + }, + { + "name": "location_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "locationid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "assetnumber" + } + }, + { + "name": "type_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "typeid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "vendor_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "vendorid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] }, "post": { "tags": [ @@ -20269,7 +28852,66 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "assetnumber": "MAINT-DATE-1", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "machine", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "machine": { + "assetid": 1, + "controllermodelid": null, + "controllervendorid": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "islocationonly": false, + "lastmaintenancedate": "2026-01-01", + "machineid": 1, + "machinetypeid": null, + "maintenanceintervaldays": null, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextmaintenancedate": "2026-01-01", + "requiresmanualconfig": false, + "vendorid": null + }, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Maintenance date probe", + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 + }, + "message": "Machine created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -20296,6 +28938,12 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "assetnumber": "MAINT-DATE-1", + "lastmaintenancedate": "2026-01-01", + "name": "Maintenance date probe", + "nextmaintenancedate": "2026-01-01" } } } @@ -20319,7 +28967,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -20368,7 +29018,68 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "assetnumber": "2001", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "machine", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "dualpathpartner": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "machine": { + "assetid": 1, + "controllermodelid": null, + "controllervendorid": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "imageurl": "/api/models/images/model-1.png", + "isactive": true, + "islocationonly": false, + "lastmaintenancedate": null, + "machineid": 1, + "machinetypeid": null, + "maintenanceintervaldays": null, + "modelname": "IMG-MODEL-1", + "modelnumberid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextmaintenancedate": null, + "requiresmanualconfig": false, + "vendorid": null + }, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -20417,7 +29128,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -20480,7 +29193,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -20531,7 +29246,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -20569,7 +29294,26 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "bystatus": [ + { + "count": 1, + "status": "In Use" + } + ], + "bytype": [], + "total": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -20607,7 +29351,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -20625,7 +29387,51 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] }, "post": { "tags": [ @@ -20643,7 +29449,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -20693,7 +29501,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -20742,7 +29552,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -20791,7 +29603,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -20854,7 +29668,75 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "assetid": 26, + "assetnumber": "MT-PAGE-05", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "measuring_tool", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "measuringtool": { + "assetid": 26, + "calibrationintervaldays": null, + "calibrationprovider": null, + "calibrationstatus": "overdue", + "calibrationstatuscolor": "#F44336", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "lastcalibrationdate": null, + "measuringtoolid": 26, + "measuringtooltypeid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextcalibrationdate": "2026-01-01", + "notes": null + }, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Paged tool 5", + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 26, + "serialnumber": "", + "statuscolor": null, + "statusid": 1, + "statusname": "In Use" + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 2, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -20872,7 +29754,91 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "assetnumber", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "calibrationstatus", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "locationid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "statusid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "typeid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] }, "post": { "tags": [ @@ -20890,7 +29856,68 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "assetnumber": "MT-001", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "measuring_tool", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": "GL-42", + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "measuringtool": { + "assetid": 1, + "calibrationintervaldays": 365, + "calibrationprovider": "Metro Cal Lab", + "calibrationstatus": "current", + "calibrationstatuscolor": "#4CAF50", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "lastcalibrationdate": "2026-01-01", + "measuringtoolid": 1, + "measuringtooltypecolor": "#14abef", + "measuringtooltypeid": 1, + "measuringtooltypename": "Caliper", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextcalibrationdate": "2026-01-01", + "notes": null + }, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Bench caliper", + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statuscolor": null, + "statusid": 1, + "statusname": "In Use" + }, + "message": "Measuring tool created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -20917,6 +29944,18 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "assetnumber": "MT-001", + "calibrationintervaldays": 365, + "calibrationprovider": "Metro Cal Lab", + "gaugelabreference": "GL-42", + "lastcalibrationdate": "2026-01-01", + "measuringtooltypeid": 1, + "name": "Bench caliper", + "nextcalibrationdate": "2026-01-01", + "serialnumber": "", + "statusid": 1 } } } @@ -20940,7 +29979,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "message": "Measuring tool deleted", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -20989,7 +30038,67 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "assetnumber": "MT-001", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "measuring_tool", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": "GL-42", + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "measuringtool": { + "assetid": 1, + "calibrationintervaldays": 365, + "calibrationprovider": "Metro Cal Lab", + "calibrationstatus": "current", + "calibrationstatuscolor": "#4CAF50", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "lastcalibrationdate": "2026-01-01", + "measuringtoolid": 1, + "measuringtooltypecolor": "#14abef", + "measuringtooltypeid": 1, + "measuringtooltypename": "Caliper", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextcalibrationdate": "2026-01-01", + "notes": null + }, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Bench caliper", + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statuscolor": null, + "statusid": 1, + "statusname": "In Use" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -21038,7 +30147,66 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 20, + "assetnumber": "MT-AUDIT-1", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "measuring_tool", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "measuringtool": { + "assetid": 20, + "calibrationintervaldays": null, + "calibrationprovider": null, + "calibrationstatus": "current", + "calibrationstatuscolor": "#4CAF50", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "lastcalibrationdate": "2026-01-01", + "measuringtoolid": 20, + "measuringtooltypeid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "nextcalibrationdate": "2026-01-01", + "notes": null + }, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "After rename", + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 20, + "serialnumber": "", + "statuscolor": null, + "statusid": 1, + "statusname": "In Use" + }, + "message": "Measuring tool updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -21078,6 +30246,11 @@ "type": "object", "additionalProperties": true, "description": "path: tool_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "calibrationprovider": "In-house", + "name": "Renamed tool", + "nextcalibrationdate": "2026-01-01" } } } @@ -21101,7 +30274,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -21152,7 +30327,33 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "assetid": 4, + "color": "#F44336", + "label": "Overdue" + }, + { + "assetid": 6, + "color": "#F44336", + "label": "Overdue" + }, + { + "assetid": 16, + "color": "#F44336", + "label": "Overdue" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -21190,7 +30391,36 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "buckets": { + "current": [], + "duesoon": [], + "overdue": [], + "unknown": [] + }, + "counts": { + "current": 0, + "duesoon": 0, + "overdue": 0, + "unknown": 0 + }, + "statuscolors": { + "current": "#4CAF50", + "duesoon": "#FF9800", + "overdue": "#F44336", + "unknown": "#9E9E9E" + } + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -21228,7 +30458,54 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "color": "#7934f3", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "Bore / hole diameter gage", + "isactive": true, + "measuringtooltypeid": 4, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Bore Gage" + }, + { + "color": "#14abef", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "Vernier / digital caliper", + "isactive": true, + "measuringtooltypeid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Caliper" + }, + { + "color": "#e83e8c", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "Reference gage block set", + "isactive": true, + "measuringtooltypeid": 7, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Gage Block Set" + }, + "" + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 8, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -21246,7 +30523,51 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] }, "post": { "tags": [ @@ -21264,7 +30585,26 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "color": "#123456", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "Pin gage set", + "isactive": true, + "measuringtooltypeid": 9, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Pin Gage" + }, + "message": "Measuring-tool type created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -21291,6 +30631,11 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "color": "#123456", + "description": "Pin gage set", + "name": "Pin Gage" } } } @@ -21314,7 +30659,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "message": "Measuring-tool type deleted", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -21363,7 +30718,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -21412,7 +30769,26 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "color": "#123456", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": "Updated", + "isactive": true, + "measuringtooltypeid": 9, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Pin Gage" + }, + "message": "Measuring-tool type updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -21452,6 +30828,9 @@ "type": "object", "additionalProperties": true, "description": "path: type_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "description": "Updated" } } } @@ -21475,7 +30854,77 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "assetid": 1, + "assetnumber": "NET-1", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "network_device", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "ipaddress": "", + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "networkdevice": { + "assetid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "firmwareversion": null, + "hostname": "", + "isactive": true, + "ismanaged": false, + "ispoe": false, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "networkdeviceid": 1, + "networkdevicetypeid": 1, + "networkdevicetypename": "Switch", + "portcount": null, + "rackunit": null, + "vendorid": 1, + "vendorname": "Cisco" + }, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -21493,7 +30942,157 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "assetnumber", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "businessunit_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "businessunitid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "dir", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "asc" + } + }, + { + "name": "location_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "locationid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "managed", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "poe", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "hostname" + } + }, + { + "name": "type_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "typeid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "vendor_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "vendorid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] }, "post": { "tags": [ @@ -21511,7 +31110,67 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "assetnumber": "APNL-Dock-Door-4", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "network_device", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "ipaddress": "", + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Dock Door 4", + "networkdevice": { + "assetid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "firmwareversion": null, + "hostname": "", + "isactive": true, + "ismanaged": false, + "ispoe": false, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "networkdeviceid": 1, + "networkdevicetypeid": 1, + "networkdevicetypename": "Access Panel", + "portcount": null, + "rackunit": null, + "vendorid": null + }, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 + }, + "message": "Network device created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -21538,6 +31197,12 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "assetnumber": "SW-CORE-01", + "hostname": "", + "ipaddress": "", + "name": "Core switch" } } } @@ -21561,7 +31226,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -21610,7 +31277,65 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "assetnumber": "AST-networkdevice", + "assettypecolor": null, + "assettypeid": 3, + "assettypename": "network_device", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": "GL-2", + "ipaddress": "", + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": "MNT-2", + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "networkdevice": { + "assetid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "firmwareversion": null, + "hostname": "", + "isactive": true, + "ismanaged": false, + "ispoe": false, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "networkdeviceid": 1, + "networkdevicetypeid": null, + "portcount": null, + "rackunit": null, + "vendorid": null + }, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -21659,7 +31384,66 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "assetnumber": "AST-networkdevice", + "assettypecolor": null, + "assettypeid": 3, + "assettypename": "network_device", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": "GL-2", + "ipaddress": "", + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": "MNT-2", + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "networkdevice": { + "assetid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "firmwareversion": null, + "hostname": "", + "isactive": true, + "ismanaged": false, + "ispoe": false, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "networkdeviceid": 1, + "networkdevicetypeid": null, + "portcount": null, + "rackunit": null, + "vendorid": null + }, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "serialnumber": "", + "statusid": 1 + }, + "message": "Network device updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -21699,6 +31483,10 @@ "type": "object", "additionalProperties": true, "description": "path: device_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "gaugelabreference": "GL-2", + "maintenancereference": "MNT-2" } } } @@ -21722,7 +31510,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -21773,7 +31563,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -21824,7 +31616,23 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "bytype": [], + "byvendor": [], + "nonpoe": 0, + "poe": 0, + "total": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -21862,7 +31670,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -21880,7 +31706,75 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "locationid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "type", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "vlanid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] }, "post": { "tags": [ @@ -21898,7 +31792,40 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "broadcastaddress": null, + "cidr": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "dhcpenabled": true, + "dhcprangeend": null, + "dhcprangestart": null, + "dns1": null, + "dns2": null, + "gatewayip": null, + "isactive": true, + "locationid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "Attached", + "networkaddress": null, + "subnetid": 1, + "subnetmask": null, + "subnettype": null, + "vlanid": 1, + "vlanname": "In use", + "vlannumber": 77 + }, + "message": "Subnet created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -21925,6 +31852,11 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "cidr": "", + "name": "Attached", + "vlanid": 1 } } } @@ -21948,7 +31880,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -21997,7 +31931,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -22046,7 +31982,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -22109,7 +32047,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -22127,7 +32083,51 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] }, "post": { "tags": [ @@ -22145,7 +32145,28 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "color": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "icon": null, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "networkdevicetype": "Door Controller", + "networkdevicetypeid": 1, + "prefix": "DOOR" + }, + "message": "Network device type created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -22172,6 +32193,10 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "networkdevicetype": "Door Controller", + "prefix": "DOOR" } } } @@ -22195,7 +32220,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -22244,7 +32271,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -22293,7 +32322,28 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "color": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "icon": null, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "networkdevicetype": "Door Controller", + "networkdevicetypeid": 1, + "prefix": "DR" + }, + "message": "Network device type updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -22333,6 +32383,9 @@ "type": "object", "additionalProperties": true, "description": "path: type_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "prefix": "DR" } } } @@ -22356,7 +32409,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -22374,7 +32445,59 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "type", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] }, "post": { "tags": [ @@ -22392,7 +32515,28 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "createddate": "2026-01-01T00:00:00.000000Z", + "description": null, + "isactive": true, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": "VLAN 4094", + "subnetcount": 0, + "vlanid": 1, + "vlannumber": 4094, + "vlantype": null + }, + "message": "VLAN created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -22419,6 +32563,10 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "name": "VLAN 4094", + "vlannumber": 4094 } } } @@ -22442,7 +32590,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -22491,7 +32641,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -22540,7 +32692,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -22603,7 +32757,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -22621,7 +32793,84 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "current", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "false" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "ticketnumber", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "type_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "typeid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] }, "post": { "tags": [ @@ -22639,7 +32888,49 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "alertfleet": true, + "alertscope": { + "computertype": "CMM PC", + "hostname": [ + "", + "" + ] + }, + "alerttimeout": 0, + "appid": null, + "businessunitid": null, + "employeename": "", + "employeesso": "", + "enddate": null, + "endtime": null, + "isactive": true, + "iscurrent": true, + "isshopfloor": false, + "link": null, + "linkurl": null, + "message": "Line 3 is down", + "notification": "Line 3 is down", + "notificationid": 1, + "notificationtypeid": 1, + "startdate": "2026-01-01T00:00:00.000000+00:00", + "starttime": "2026-01-01T00:00:00.000000+00:00", + "ticketnumber": null, + "title": "Line 3 is down", + "typecolor": "#d93f3f", + "typename": "Fleet Alert" + }, + "message": "Notification created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -22666,6 +32957,18 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "alertfleet": true, + "alertscope": { + "computertype": "CMM PC", + "hostname": [ + "", + "" + ] + }, + "notification": "Line 3 is down", + "notificationtypeid": 1 } } } @@ -22689,7 +32992,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -22738,7 +33043,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -22787,7 +33094,43 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "alertfleet": false, + "alertscope": null, + "alerttimeout": 0, + "appid": null, + "businessunitid": null, + "employeename": "", + "employeesso": "", + "enddate": null, + "endtime": null, + "isactive": true, + "iscurrent": true, + "isshopfloor": false, + "link": null, + "linkurl": null, + "message": "Line 3 is down", + "notification": "Line 3 is down", + "notificationid": 1, + "notificationtypeid": 1, + "startdate": "2026-01-01T00:00:00.000000+00:00", + "starttime": "2026-01-01T00:00:00.000000+00:00", + "ticketnumber": null, + "title": "Line 3 is down", + "typecolor": "#d93f3f", + "typename": "Fleet Alert" + }, + "message": "Notification updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -22827,6 +33170,10 @@ "type": "object", "additionalProperties": true, "description": "path: notification_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "alertfleet": null, + "alerttimeout": null } } } @@ -22850,7 +33197,20 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "notifications": [], + "total": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -22888,7 +33248,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -22906,7 +33276,25 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "end", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "start", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/notifications/dashboard/active": { @@ -22926,7 +33314,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -22964,7 +33362,20 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "active": 0, + "bytype": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -22998,7 +33409,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -23039,7 +33452,61 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "configversion": "2b7c2d2f543f", + "current": [ + { + "boardcategory": "Current Events", + "boardorder": 50, + "displaystyle": "grid", + "employeename": "", + "employeepicture": "", + "employeesso": "", + "endtime": null, + "isactive": true, + "isshopfloor": true, + "link": null, + "notification": "press down", + "notificationid": 2, + "resolved": false, + "starttime": null, + "ticketnumber": null, + "typecolor": "#17a2b8", + "typename": "Incident" + }, + { + "boardcategory": "Current Events", + "boardorder": 30, + "displaystyle": "grid", + "employeename": "", + "employeepicture": "", + "employeesso": "", + "endtime": null, + "isactive": true, + "isshopfloor": true, + "link": null, + "notification": "retooling", + "notificationid": 1, + "resolved": false, + "starttime": null, + "ticketnumber": null, + "typecolor": "#17a2b8", + "typename": "Change" + } + ], + "timestamp": "2026-01-01T00:00:00.000000", + "upcoming": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -23051,7 +33518,17 @@ } } } - } + }, + "parameters": [ + { + "name": "businessunit", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/notifications/types": { @@ -23071,7 +33548,43 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "boardcategory": "Alerts", + "boardorder": 100, + "displaystyle": "grid", + "expirydays": null, + "expiryhour": null, + "expiryminute": 0, + "expirymode": "none", + "gracewindowminutes": 0, + "isactive": true, + "notificationtypeid": 1, + "showemployeephoto": "", + "splitperemployee": "", + "typecolor": "#17a2b8", + "typedescription": null, + "typename": "Awareness" + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -23089,7 +33602,43 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] }, "post": { "tags": [ @@ -23107,7 +33656,34 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "boardcategory": "Alerts", + "boardorder": 100, + "displaystyle": "grid", + "expirydays": null, + "expiryhour": null, + "expiryminute": 0, + "expirymode": "none", + "gracewindowminutes": 0, + "isactive": true, + "notificationtypeid": 1, + "showemployeephoto": "", + "splitperemployee": "", + "typecolor": "#17a2b8", + "typedescription": null, + "typename": "Awareness" + }, + "message": "Notification type created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -23134,6 +33710,11 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "boardcategory": "Alerts", + "displaystyle": "grid", + "typename": "Awareness" } } } @@ -23157,7 +33738,34 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "boardcategory": "Alerts", + "boardorder": 100, + "displaystyle": "grid", + "expirydays": null, + "expiryhour": null, + "expiryminute": 0, + "expirymode": "none", + "gracewindowminutes": 0, + "isactive": true, + "notificationtypeid": 1, + "showemployeephoto": "", + "splitperemployee": "", + "typecolor": "#17a2b8", + "typedescription": null, + "typename": "Awareness" + }, + "message": "Notification type updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -23197,6 +33805,9 @@ "type": "object", "additionalProperties": true, "description": "path: type_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "boardcategory": "Alerts" } } } @@ -23218,7 +33829,34 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "boardcategory": "Alerts", + "boardorder": 100, + "displaystyle": "grid", + "expirydays": null, + "expiryhour": null, + "expiryminute": 0, + "expirymode": "none", + "gracewindowminutes": 0, + "isactive": true, + "notificationtypeid": 1, + "showemployeephoto": "", + "splitperemployee": "", + "typecolor": "#17a2b8", + "typedescription": null, + "typename": "Awareness" + }, + "message": "Notification type updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -23258,6 +33896,9 @@ "type": "object", "additionalProperties": true, "description": "path: type_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "boardcategory": "Alerts" } } } @@ -23281,7 +33922,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -23332,7 +33975,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -23379,7 +34024,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -23425,7 +34072,43 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "binlocation": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "gagelabtag": "WJRP0117", + "imageurl": null, + "isactive": true, + "islowstock": true, + "itemcode": "3DP0001", + "itemdescription": null, + "itemname": "Gage block holder", + "latestrevision": null, + "lowstockthreshold": 5, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "printeditemid": 1, + "printnotes": null, + "quantityonhand": 0 + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -23443,7 +34126,60 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "lowstock", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] }, "post": { "tags": [ @@ -23461,7 +34197,34 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "binlocation": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "gagelabtag": "WJRP0117", + "imageurl": null, + "isactive": true, + "islowstock": true, + "itemcode": "3DP0001", + "itemdescription": null, + "itemname": "Gage block holder", + "latestrevision": null, + "lowstockthreshold": 5, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "printeditemid": 1, + "printnotes": null, + "quantityonhand": 0 + }, + "message": "Printed item created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -23488,6 +34251,10 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "gagelabtag": "wjrp0117", + "itemname": "Gage block holder" } } } @@ -23511,7 +34278,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "message": "Printed item retired", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -23560,7 +34337,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -23609,7 +34388,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -23672,7 +34453,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -23735,7 +34518,38 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "fileid": 2, + "filename": "clip_v2.gcode", + "filesize": 8, + "printeditemid": 1, + "revision": 2, + "uploadedby": "testadmin", + "uploadeddate": "2026-01-01T00:00:00.000000Z", + "uploadnote": null + }, + { + "fileid": 1, + "filename": "clip_v1.stl", + "filesize": 10, + "printeditemid": 1, + "revision": 1, + "uploadedby": "testadmin", + "uploadeddate": "2026-01-01T00:00:00.000000Z", + "uploadnote": "initial" + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -23784,7 +34598,27 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "fileid": 1, + "filename": "clip_v1.stl", + "filesize": 10, + "printeditemid": 1, + "revision": 1, + "uploadedby": "testadmin", + "uploadeddate": "2026-01-01T00:00:00.000000Z", + "uploadnote": "initial" + }, + "message": "Revision 1 uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -23847,7 +34681,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -23896,7 +34732,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -23959,7 +34797,34 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "binlocation": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "gagelabtag": null, + "imageurl": null, + "isactive": true, + "islowstock": false, + "itemcode": "3DP-9001", + "itemdescription": null, + "itemname": "Test clip", + "latestrevision": null, + "lowstockthreshold": 5, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "printeditemid": 1, + "printnotes": null, + "quantityonhand": 10 + }, + "message": "Stock added", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -23999,6 +34864,10 @@ "type": "object", "additionalProperties": true, "description": "path: item_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "badge": "", + "quantity": 10 } } } @@ -24022,7 +34891,34 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "binlocation": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "gagelabtag": null, + "imageurl": null, + "isactive": true, + "islowstock": true, + "itemcode": "3DP-9001", + "itemdescription": null, + "itemname": "Test clip", + "latestrevision": null, + "lowstockthreshold": 5, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "printeditemid": 1, + "printnotes": null, + "quantityonhand": 0 + }, + "message": "Printed item restored", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -24086,7 +34982,33 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "binlocation": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "gagelabtag": "WJRP0117", + "imageurl": null, + "isactive": true, + "islowstock": true, + "itemcode": "3DP0001", + "itemdescription": null, + "itemname": "Gage block holder", + "latestrevision": null, + "lowstockthreshold": 5, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "printeditemid": 1, + "printnotes": null, + "quantityonhand": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -24131,7 +35053,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -24170,7 +35094,34 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "binlocation": null, + "createddate": "2026-01-01T00:00:00.000000Z", + "gagelabtag": "WJRP7000", + "imageurl": null, + "isactive": true, + "islowstock": false, + "itemcode": "3DP-7000", + "itemdescription": null, + "itemname": "Rev part", + "latestrevision": null, + "lowstockthreshold": 2, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "printeditemid": 1, + "printnotes": null, + "quantityonhand": 8 + }, + "message": "Took 1, 8 left", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -24191,6 +35142,11 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "badge": "", + "itemcode": "WJRP7000|3", + "quantity": 1 } } } @@ -24214,7 +35170,26 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "columns": [ + "employeesso", + "employeename", + "takes", + "" + ], + "days": 30, + "rows": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -24232,7 +35207,26 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "days", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 30 + } + }, + { + "name": "format", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/printedparts/reports/consumption": { @@ -24252,7 +35246,26 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "columns": [ + "itemcode", + "itemname", + "takes", + "" + ], + "days": 30, + "rows": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -24270,7 +35283,26 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "days", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 30 + } + }, + { + "name": "format", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/printedparts/reports/stock": { @@ -24290,7 +35322,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "columns": [ + "itemcode", + "itemname", + "binlocation", + "" + ], + "rows": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -24308,7 +35358,17 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "format", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/printers": { @@ -24328,7 +35388,80 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "assetid": 1, + "assetnumber": "PR-1", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "printer", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "ipaddress": "", + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "printer": { + "assetid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "hostname": "", + "installpath": null, + "isactive": true, + "iscolor": false, + "iscsf": false, + "isduplex": false, + "isnetwork": true, + "modelname": "ZT411", + "modelnumberid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "pin": null, + "printerid": 1, + "printertypeid": 1, + "printertypename": "Thermal", + "sharename": null, + "vendorid": null, + "windowsname": null + }, + "serialnumber": "", + "statusid": 1 + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -24346,7 +35479,141 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "assetnumber", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "businessunit_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "businessunitid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "dir", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "asc" + } + }, + { + "name": "location_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "locationid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "hostname" + } + }, + { + "name": "type_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "typeid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "vendor_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "vendorid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] }, "post": { "tags": [ @@ -24364,7 +35631,71 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "assetnumber": "SpoolsInspection-Xerox", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "printer", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "printer": { + "assetid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "hostname": "", + "installpath": null, + "isactive": true, + "iscolor": false, + "iscsf": false, + "isduplex": false, + "isnetwork": true, + "modelname": "VersaLink C7125", + "modelnumberid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "pin": null, + "printerid": 1, + "printertypeid": null, + "sharename": null, + "vendorfrommodel": true, + "vendorid": null, + "vendorname": "Xerox", + "windowsname": null + }, + "serialnumber": "", + "statusid": 1 + }, + "message": "Printer created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -24391,6 +35722,13 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "assetnumber": "CSF01-Materials-HP", + "hostname": "", + "installpath": "", + "mapx": 120, + "mapy": 240 } } } @@ -24414,7 +35752,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -24463,7 +35803,71 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "assetnumber": "PR-2001", + "assettypecolor": null, + "assettypeid": 1, + "assettypename": "printer", + "businessunitid": null, + "communications": [], + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "drivers": [], + "gaugelabreference": null, + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": null, + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "printer": { + "assetid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "hostname": "", + "imageurl": "/api/models/images/model-1.png", + "installpath": null, + "isactive": true, + "iscolor": false, + "iscsf": false, + "isduplex": false, + "isnetwork": true, + "modelname": "IMG-MODEL-1", + "modelnumberid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "pin": null, + "printerid": 1, + "printertypeid": null, + "sharename": null, + "vendorid": null, + "windowsname": null + }, + "serialnumber": "", + "statusid": 1 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -24512,7 +35916,68 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "assetnumber": "AST-printer", + "assettypecolor": null, + "assettypeid": 2, + "assettypename": "printer", + "businessunitid": null, + "createdby": "", + "createddate": "2026-01-01T00:00:00.000000Z", + "deletedby": null, + "deleteddate": null, + "gaugelabreference": "GL-2", + "isactive": true, + "levelid": null, + "locationcode": null, + "locationid": null, + "maintenancereference": "MNT-2", + "mapverifiedat": null, + "mapx": null, + "mapy": null, + "modifiedby": "", + "modifieddate": "2026-01-01T00:00:00.000000Z", + "name": null, + "notes": null, + "ownlevelid": null, + "ownlocationid": null, + "ownmapx": null, + "ownmapy": null, + "pluginid": 1, + "printer": { + "assetid": 1, + "createddate": "2026-01-01T00:00:00.000000Z", + "hostname": "", + "installpath": null, + "isactive": true, + "iscolor": false, + "iscsf": false, + "isduplex": false, + "isnetwork": true, + "modelnumberid": null, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "pin": null, + "printerid": 1, + "printertypeid": null, + "sharename": null, + "vendorid": null, + "windowsname": null + }, + "serialnumber": "", + "statusid": 1 + }, + "message": "Printer updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -24552,6 +36017,10 @@ "type": "object", "additionalProperties": true, "description": "path: printer_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "gaugelabreference": "GL-2", + "maintenancereference": "MNT-2" } } } @@ -24575,7 +36044,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -24626,7 +36097,21 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 4, + "defaultprinterassetid": null, + "printerassetids": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -24675,7 +36160,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 3, + "defaultprinterassetid": 4, + "printerassetids": [ + 4, + 5 + ] + }, + "message": "Printer assignment updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -24715,6 +36218,13 @@ "type": "object", "additionalProperties": true, "description": "path: asset_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "defaultprinterassetid": 4, + "printerassetids": [ + 4, + 5 + ] } } } @@ -24738,7 +36248,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -24801,7 +36313,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -24852,7 +36366,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "bytype": [], + "byvendor": [], + "criticalsupplies": 0, + "lowsupplies": 0, + "online": 0, + "total": 0, + "totalprinters": 0 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -24890,7 +36422,73 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "rows": [ + { + "iscritical": true, + "levelid": null, + "location": "Shop floor", + "mapx": null, + "mapy": null, + "printerid": 2, + "printername": "Bravo", + "supplies": [ + { + "level": "critical", + "text": "Black Toner 0%", + "title": "No part number on file for this model" + } + ], + "worstremaining": 0 + }, + { + "iscritical": true, + "levelid": null, + "location": "Shop floor", + "mapx": null, + "mapy": null, + "printerid": 3, + "printername": "Charlie", + "supplies": [ + { + "level": "critical", + "text": "Black Toner 2%", + "title": "No part number on file for this model" + } + ], + "worstremaining": 2 + }, + { + "iscritical": true, + "levelid": null, + "location": "Shop floor", + "mapx": null, + "mapy": null, + "printerid": 1, + "printername": "Alpha", + "supplies": [ + { + "level": "critical", + "text": "Black Toner 4%", + "title": "No part number on file for this model" + } + ], + "worstremaining": 4 + }, + "" + ], + "unavailable": null + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -24928,7 +36526,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -24946,7 +36554,18 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + } + ] }, "post": { "tags": [ @@ -24964,7 +36583,28 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "description": null, + "driverid": 1, + "drivername": "HP Universal Printing PCL 6", + "isactive": true, + "location": "", + "modelname": null, + "modelnumberid": null, + "name": "HP universal", + "vendorid": 1 + }, + "message": "Driver created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -24991,6 +36631,12 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "drivername": "HP Universal Printing PCL 6", + "location": "", + "name": "HP universal", + "vendorid": 1 } } } @@ -25014,7 +36660,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -25063,7 +36711,28 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "description": null, + "driverid": 1, + "drivername": "Xerox Global Print Driver PCL6", + "isactive": true, + "location": "", + "modelname": null, + "modelnumberid": null, + "name": "Xerox universal", + "vendorid": 2 + }, + "message": "Driver updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -25103,6 +36772,10 @@ "type": "object", "additionalProperties": true, "description": "path: driver_id (int); body: JSON (fields not yet documented)" + }, + "example": { + "drivername": "Xerox Global Print Driver PCL6", + "vendorid": 2 } } } @@ -25127,7 +36800,73 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "assetnumber": "1001", + "defaultprinterid": null, + "hostname": "", + "printers": [ + { + "assetid": 4, + "driverid": null, + "driverlocation": null, + "drivername": null, + "hostname": "", + "inheritedfromassetid": 3, + "installpath": null, + "ipaddress": "", + "isdefault": false, + "port": null, + "printerid": 1, + "queuename": "PRINTER-A", + "sharename": null, + "windowsname": "PRINTER-A" + }, + { + "assetid": 5, + "driverid": null, + "driverlocation": null, + "drivername": null, + "hostname": "", + "inheritedfromassetid": 3, + "installpath": null, + "ipaddress": "", + "isdefault": false, + "port": null, + "printerid": 2, + "queuename": "PRINTER-B", + "sharename": null, + "windowsname": "PRINTER-B" + }, + { + "assetid": 6, + "driverid": null, + "driverlocation": null, + "drivername": null, + "hostname": "", + "inheritedfromassetid": 3, + "installpath": null, + "ipaddress": "", + "isdefault": false, + "port": null, + "printerid": 3, + "queuename": "PRINTER-C", + "sharename": null, + "windowsname": "PRINTER-C" + } + ], + "source": "inherited" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -25173,7 +36912,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -25185,7 +36926,17 @@ } } } - } + }, + "parameters": [ + { + "name": "printerids", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/printers/install-list": { @@ -25206,7 +36957,35 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "hostname": "", + "installpath": "", + "ipaddress": "", + "iscsf": false, + "levelid": null, + "locationname": null, + "machinenumber": "CSF01-Materials-HP", + "mapx": 120, + "mapy": 240, + "modelnumber": null, + "name": "CSF01-Materials-HP", + "printerid": 1, + "sharename": null, + "vendorname": "", + "windowsname": null + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -25218,7 +36997,17 @@ } } } - } + }, + "parameters": [ + { + "name": "format", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/printers/lookup": { @@ -25238,7 +37027,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -25256,7 +37047,25 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "fqdn", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "ip", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/printers/lowsupplies": { @@ -25276,7 +37085,24 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "printers": [], + "summary": { + "critical": 0, + "low": 0, + "total_checked": 0 + } + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -25314,7 +37140,33 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "modelnumber": "TestModel C999", + "modelnumberid": 1, + "supplycount": 1, + "vendor": "TestVendor", + "vendorid": 1 + } + ], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 1, + "totalpages": 1 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -25332,7 +37184,67 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "vendor_id", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "vendorid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "withsupplies", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "" + } + } + ] } }, "/api/printers/models/{modelnumberid}/supplies": { @@ -25352,7 +37264,37 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "modelnumber": "TestModel C999", + "modelnumberid": 1, + "supplies": [ + { + "capacitytier": "standard", + "color": "black", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "marketingname": "414A Black", + "modelnumber": "TestModel C999", + "modelnumberid": 1, + "modelsupplyid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "pageyield": 2400, + "partnumber": "W2020A", + "supplytype": "toner" + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -25401,7 +37343,32 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "capacitytier": "standard", + "color": "black", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "marketingname": "414A Black", + "modelnumber": "TestModel C999", + "modelnumberid": 1, + "modelsupplyid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "pageyield": 2400, + "partnumber": "W2020A", + "supplytype": "toner" + }, + "message": "Supply added", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -25441,6 +37408,14 @@ "type": "object", "additionalProperties": true, "description": "path: modelnumberid (int); body: JSON (fields not yet documented)" + }, + "example": { + "capacitytier": "standard", + "color": "black", + "marketingname": "414A Black", + "pageyield": 2400, + "partnumber": "W2020A", + "supplytype": "toner" } } } @@ -25464,7 +37439,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -25515,7 +37492,108 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assetid": 1, + "assetnumber": "1001", + "hosts": [ + { + "assetid": 1, + "assetnumber": "1001", + "hostname": "", + "observedat": "2026-01-01T00:00:00.000000", + "queues": [ + { + "classification": "extra", + "driftfields": [], + "drivername": "HP Universal Printing PS", + "expecteddrivername": null, + "expectedportaddress": null, + "expectedqueuename": null, + "inheritedfromassetid": null, + "isassigneddefault": false, + "isdefault": true, + "isshared": false, + "portaddress": "", + "portname": "IP_10.20.0.11", + "printerassetid": 5, + "printerid": 1, + "printername": "Bay label printer", + "queuename": "PRINTER-A" + }, + { + "classification": "extra", + "driftfields": [], + "drivername": "HP Universal Printing PS", + "expecteddrivername": null, + "expectedportaddress": null, + "expectedqueuename": null, + "inheritedfromassetid": null, + "isassigneddefault": false, + "isdefault": false, + "isshared": false, + "portaddress": "", + "portname": "IP_10.20.0.12", + "printerassetid": 6, + "printerid": 2, + "printername": "Bay laser printer", + "queuename": "PRINTER-B" + }, + { + "classification": "unknown", + "driftfields": [], + "drivername": "HP Universal Printing PS", + "expecteddrivername": null, + "expectedportaddress": null, + "expectedqueuename": null, + "inheritedfromassetid": null, + "isassigneddefault": false, + "isdefault": false, + "isshared": false, + "portaddress": "", + "portname": "IP_10.20.0.99", + "printerassetid": null, + "printerid": null, + "printername": null, + "queuename": "Reception Copier" + } + ], + "seedcandidate": { + "defaultprinterassetid": 5, + "printerassetids": [ + 5, + 6 + ], + "skipped": [ + { + "drivername": "HP Universal Printing PS", + "isdefault": false, + "portaddress": "", + "portname": "IP_10.20.0.99", + "queuename": "Reception Copier" + } + ] + }, + "source": "none", + "summary": { + "drifted": 0, + "extra": 2, + "matching": 0, + "missing": 0, + "unknown": 1 + } + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -25567,7 +37645,20 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "printerid": 1, + "windowsname": "HP-CSF04-Materials" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -25579,7 +37670,25 @@ } } } - } + }, + "parameters": [ + { + "name": "format", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "machine", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/printers/supplies/{modelsupplyid}": { @@ -25599,7 +37708,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "message": "Supply deleted", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -25648,7 +37767,32 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "capacitytier": "high", + "color": "black", + "createddate": "2026-01-01T00:00:00.000000Z", + "isactive": true, + "marketingname": "414X Black", + "modelnumber": "TestModel C999", + "modelnumberid": 1, + "modelsupplyid": 1, + "modifieddate": "2026-01-01T00:00:00.000000Z", + "notes": null, + "pageyield": null, + "partnumber": "W2020A", + "supplytype": "toner" + }, + "message": "Supply updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -25688,6 +37832,10 @@ "type": "object", "additionalProperties": true, "description": "path: modelsupplyid (int); body: JSON (fields not yet documented)" + }, + "example": { + "capacitytier": "high", + "marketingname": "414X Black" } } } @@ -25711,7 +37859,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "available": false, + "cartridges": [], + "days": 90, + "horizondays": 14, + "orderlist": [], + "reason": "Zabbix is not configured or not reachable", + "unestimated": [] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -25729,7 +37895,18 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "days", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 90 + } + } + ] } }, "/api/printers/supplies/meta": { @@ -25749,7 +37926,36 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "capacitytiers": [ + "standard", + "high", + "extrahigh", + "" + ], + "colors": [ + "black", + "cyan", + "magenta", + "" + ], + "supplytypes": [ + "toner", + "drum", + "waste", + "" + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -25787,7 +37993,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "message": "Supply cache cleared", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -25814,7 +38030,8 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" - } + }, + "example": {} } } } @@ -25837,7 +38054,25 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "pagination": { + "hasnext": false, + "hasprev": false, + "page": 1, + "perpage": 20, + "total": 0, + "totalpages": 0 + }, + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -25855,7 +38090,51 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] }, "post": { "tags": [ @@ -25873,7 +38152,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -25923,7 +38204,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -25972,7 +38255,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -26021,7 +38306,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -26084,7 +38371,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -26135,7 +38432,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -26206,7 +38505,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -26269,7 +38570,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -26332,7 +38635,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -26388,10 +38693,16 @@ "security": [], "responses": { "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "description": "Success. This route does NOT return the standard envelope; the example is a transcript of a real response.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "example": { + "basepath": "/api/slides/img/lobby/", + "interval": 10, + "slides": [], + "success": true, + "surface": "lobby" + } } } }, @@ -26403,7 +38714,18 @@ } } } - } + }, + "parameters": [ + { + "name": "surface", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "lobby" + } + } + ] } }, "/api/slides/img/{surface}/{filename}": { @@ -26419,7 +38741,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -26474,7 +38798,23 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "createddate": "2026-01-01T00:00:00.000000", + "expiresat": "2026-01-01T00:00:00.000000", + "token": "", + "viewcount": 0, + "viewsremaining": 100 + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -26495,6 +38835,11 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "ciphertext": "", + "iv": "MTIzNDU2Nzg5MDEy", + "views": 100 } } } @@ -26514,7 +38859,19 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "available": false + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -26553,7 +38910,20 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "ciphertext": "", + "iv": "MTIzNDU2Nzg5MDEy" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -26610,7 +38980,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -26628,7 +39000,52 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "" + } + }, + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "" + } + } + ] }, "post": { "tags": [ @@ -26646,7 +39063,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -26696,7 +39115,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -26745,7 +39166,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -26808,7 +39231,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -26871,7 +39296,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -26934,7 +39361,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -26964,6 +39393,31 @@ "schema": { "type": "string" } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } } ] } @@ -26985,7 +39439,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -27048,7 +39504,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -27066,7 +39524,52 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "" + } + }, + { + "name": "badge", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "perpage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + } + ] } }, "/api/usb/checkouts/active": { @@ -27086,7 +39589,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -27104,7 +39609,18 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "badge", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "" + } + } + ] } }, "/api/usb/kiosk/checkin": { @@ -27125,7 +39641,20 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "device_id": "USB-1" + }, + "message": "USB-1 checked in", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -27146,6 +39675,11 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "badge": "", + "device_id": "USB-1", + "forsomeoneelse": true } } } @@ -27170,7 +39704,21 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "device_id": "USB-1", + "holdername": "Dana Reyes" + }, + "message": "USB-1 checked out to Dana Reyes", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -27191,6 +39739,10 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "badge": "", + "device_id": "USB-1" } } } @@ -27215,7 +39767,23 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "device_desc": "Blue Kingston", + "device_id": "USB-1", + "holdername": "Dana Reyes", + "locker_location": null, + "status": "checked-out" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -27261,7 +39829,24 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [ + { + "device_desc": "Blue Kingston", + "device_id": "USB-1", + "holdername": "Dana Reyes", + "locker_location": null + } + ], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -27282,6 +39867,9 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "badge": "" } } } @@ -27305,7 +39893,20 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "expiresinminutes": 30, + "url": "/api/usb/kiosk/setup?code=" + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -27344,7 +39945,34 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "available": [ + { + "device_desc": "Second", + "device_id": "USB-2", + "locker_location": null + } + ], + "name": "Dana Reyes", + "otherscount": 0, + "yours": [ + { + "device_desc": "Blue Kingston", + "device_id": "USB-1", + "locker_location": null + } + ] + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -27365,6 +39993,9 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "badge": "" } } } @@ -27388,7 +40019,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -27406,7 +40039,17 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "code", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/api/usb/reports/audit": { @@ -27426,7 +40069,67 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "columns": [ + "timestamp", + "action", + "device_id", + "" + ], + "days": 365, + "rows": [ + { + "action": "check-in", + "badge": "", + "device_desc": "Blue Kingston", + "device_id": "USB-1", + "locker_location": "", + "name": "Dana Reyes", + "sanitized": "", + "scanned_viruses": "", + "timestamp": "2026-01-01T00:00:00.000000" + }, + { + "action": "check-out", + "badge": "", + "device_desc": "Blue Kingston", + "device_id": "USB-1", + "locker_location": "", + "name": "Dana Reyes", + "sanitized": "", + "scanned_viruses": "", + "timestamp": "2026-01-01T00:00:00.000000" + }, + { + "action": "check-out", + "badge": "", + "device_desc": "Red SanDisk", + "device_id": "USB-2", + "locker_location": "", + "name": "Sam Okafor", + "sanitized": "", + "scanned_viruses": "", + "timestamp": "2026-01-01T00:00:00.000000" + } + ], + "summary": { + "checkins": 1, + "checkouts": 2, + "devices": 2, + "events": 3, + "people": 2 + } + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -27464,7 +40167,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -27482,7 +40195,50 @@ "403": { "description": "Authenticated, but not permitted." } - } + }, + "parameters": [ + { + "name": "active", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "true" + } + }, + { + "name": "assetid", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "servicetag", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "vendor", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] }, "post": { "tags": [ @@ -27500,7 +40256,34 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assets": [], + "enddate": "2026-01-01", + "isactive": true, + "lastcheckeddate": null, + "notes": null, + "prooffilename": null, + "proofurl": null, + "provider": "manual", + "servicelevel": null, + "servicetag": "", + "startdate": null, + "status": "active", + "statuscolor": "#4CAF50", + "vendor": "Acme", + "warrantyid": 1 + }, + "message": "Warranty created", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -27527,6 +40310,11 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" + }, + "example": { + "enddate": "2026-01-01", + "servicetag": "", + "vendor": "Acme" } } } @@ -27550,7 +40338,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -27599,7 +40389,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -27648,7 +40440,34 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "assets": [], + "enddate": null, + "isactive": true, + "lastcheckeddate": null, + "notes": null, + "prooffilename": null, + "proofurl": null, + "provider": "manual", + "servicelevel": null, + "servicetag": "", + "startdate": null, + "status": "unknown", + "statuscolor": "#9E9E9E", + "vendor": "Acme", + "warrantyid": 1 + }, + "message": "Warranty updated", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -27688,6 +40507,9 @@ "type": "object", "additionalProperties": true, "description": "path: warrantyid (int); body: JSON (fields not yet documented)" + }, + "example": { + "enddate": "" } } } @@ -27711,7 +40533,33 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "enddate": null, + "isactive": true, + "lastcheckeddate": null, + "notes": null, + "prooffilename": null, + "proofurl": null, + "provider": "manual", + "servicelevel": null, + "servicetag": "", + "startdate": null, + "status": "unknown", + "statuscolor": "#9E9E9E", + "vendor": "Dell", + "warrantyid": 1 + }, + "message": "Proof removed", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -27760,7 +40608,33 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "enddate": null, + "isactive": true, + "lastcheckeddate": null, + "notes": null, + "prooffilename": "Dell invoice 4471.pdf", + "proofurl": "/api/warranty/proof/warranty-1.pdf", + "provider": "manual", + "servicelevel": null, + "servicetag": "", + "startdate": null, + "status": "unknown", + "statuscolor": "#9E9E9E", + "vendor": "Dell", + "warrantyid": 1 + }, + "message": "Proof uploaded", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -27823,7 +40697,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -27886,7 +40762,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -27937,7 +40815,17 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": [], + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -27975,7 +40863,9 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + } } } }, @@ -28027,7 +40917,30 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "buckets": { + "active": [], + "expired": [], + "expiring": [], + "unknown": [] + }, + "counts": { + "active": 0, + "expired": 0, + "expiring": 0, + "unknown": 0 + } + }, + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -28065,7 +40978,23 @@ "description": "Success. Body is the success_response envelope: {status, data, meta}.", "content": { "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" + "schema": { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + "example": { + "data": { + "candidates": 0, + "created": 0, + "matched": 0, + "updated": 0 + }, + "message": "No PCs need a warranty lookup.", + "meta": { + "requestid": "00000000", + "timestamp": "2026-01-01T00:00:00.000000Z" + }, + "status": "success" + } } } }, @@ -28084,6 +41013,17 @@ "description": "Authenticated, but not permitted." } }, + "parameters": [ + { + "name": "all", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "false" + } + } + ], "requestBody": { "required": true, "content": { @@ -28092,7 +41032,8 @@ "type": "object", "additionalProperties": true, "description": "body: JSON (fields not yet documented)" - } + }, + "example": {} } } } diff --git a/scripts/gen_openapi.py b/scripts/gen_openapi.py index 267714ac..9a82a0c1 100644 --- a/scripts/gen_openapi.py +++ b/scripts/gen_openapi.py @@ -30,19 +30,23 @@ satisfied without a credential. See AUTH_SCHEME and _security. Derived strings come from DECORATOR_AUTH, HANDLER_AUTH and INLINE_AUTH below, so a route whose gate this cannot see says so rather than guessing. -KNOWN LIMITATION: QUERY PARAMETERS ARE NOT DERIVED. Path arguments come out of -the rule, and a body is declared for the verbs that take one, but nothing here -reads request.args out of a handler - so every route whose real interface is a -query string ('?q=', '?page=', '?status=') publishes params as 'not documented' -and an operation with no `parameters` beyond its path. That is a gap in the -generated spec, not in the route: a machine client built from this document can -call those endpoints but cannot filter, search or page them. Filling it means -either a handler sweep for request.args reads - which cannot see a key built at -runtime, so it would be right most of the time and silently wrong the rest - or -writing the query contract into the inventory's PROSE half by hand, where a -human owns it. The prose half is the honest option and is where it should land. -The one query credential that IS expressed is the kiosk setup code, because it -is auth rather than a filter and rides in as a securityScheme. +QUERY PARAMETERS ARE DERIVED TOO, and on the same terms as auth: only what the +code says. The sweep reads each view's AST for request.args reads and for the +two helpers this repository reads arguments through, and emits ONLY the ones +whose name resolves to a literal. A key built at runtime produces no parameter +rather than a guessed one - see the section above build(). They land in the +inventory as `queryparams`, which is DERIVED like auth, not prose: hand-editing +it does not survive the next --refresh. + +EXAMPLES ARE HARVESTED, NOT WRITTEN. --harvest runs the test suite with this +file loaded as a pytest plugin, records what the test client actually sent and +actually got back, redacts it and folds it into the inventory. Nothing is +composed: an invented example body is a lie in a contract that a generated +client sends verbatim. Harvesting is optional and re-runnable - a checkout with +no tests, and a checkout that has never harvested, both generate a spec. + + venv/bin/python scripts/gen_openapi.py --harvest # whole suite + venv/bin/python scripts/gen_openapi.py --harvest tests/test_core """ import json @@ -191,36 +195,100 @@ def _request_body(verb, entry): prose. That is honest, and it is the difference between a machine client that can send a body and one that cannot send anything at all. """ + harvested = entry.get('requestexample') if verb not in ('post', 'put', 'patch'): - return None + # A DELETE that reads a body. Two here do, and one of them refuses + # without {"confirm": "OK"} - so the spec described a call that cannot + # succeed, and a client built from it fails on the day. The evidence is + # a transcript of a request the suite really sent; with no transcript + # this stays silent rather than offering every DELETE a body it ignores. + # A transcript proves the CLIENT sent a body, not that the ROUTE reads + # one. The suite posts {} to routes that ignore it, and publishing that + # as "this route reads a JSON body" is a contract claim the code does + # not support: DELETE /api/endpoints/agentkey never calls get_json. + # So the handler has to show a read before the body is declared. + if harvested is None or not entry.get('readsbody'): + return None + if harvested == {} or harvested is None: + # An empty body teaches nothing and reads as "send {}", which for + # a route with required fields is wrong. Declare the body, omit the + # example. + return { + 'required': False, + 'content': {'application/json': { + 'schema': {'type': 'object', 'additionalProperties': True, + 'description': 'This route reads a JSON body.'}, + }}, + } + return { + 'required': False, + 'content': {'application/json': { + 'schema': {'type': 'object', 'additionalProperties': True, + 'description': 'This route reads a JSON body. The ' + 'example is a request that worked.'}, + 'example': harvested, + }}, + } described = entry.get('params') or '' + media = { + 'schema': { + 'type': 'object', + 'additionalProperties': True, + 'description': described or 'See the endpoint description.', + } + } + # The one thing the schema cannot say: which fields. It is an open object + # because the inventory describes bodies in prose, so a harvested example is + # the only field list a reader gets - and it is a transcript of a body the + # suite actually sent, not a guess at one. + if harvested is not None: + media['example'] = harvested return { 'required': verb in ('post', 'put'), - 'content': { - 'application/json': { - 'schema': { - 'type': 'object', - 'additionalProperties': True, - 'description': described or 'See the endpoint description.', - } - } - }, + 'content': {'application/json': media}, } -def _responses(security, parameters): +def _is_envelope(example): + """Whether a harvested response is the success_response envelope. + + The envelope is `{status: 'success', data, meta}`. Anything else - a bare + list, a plain object, a manifest - is a route that answers in its own shape. + """ + return isinstance(example, dict) and example.get('status') == 'success' + + +def _responses(security, parameters, example=None): """The envelope, plus the failures a caller has to handle. One hardcoded 200 was the whole response section, so a generated client had no idea any call could fail, and 19 operations that do not return the envelope at all were documented as if they did. + + The 200 media type carried a bare `$ref` where OpenAPI expects a Media Type + Object, i.e. `{"schema": {"$ref": ...}}`. Redoc tolerated it; a strict + parser does not, and there is nowhere to hang an example on the wrong shape. """ + envelope = 'Success. Body is the success_response envelope: {status, data, meta}.' + success = {'schema': {'$ref': '#/components/schemas/SuccessEnvelope'}} + description = envelope + if example is not None: + success['example'] = example + # A harvested example is EVIDENCE. Nineteen routes here do not return + # the envelope, and every one of them was documented as if it did - a + # $ref that a generated client validates against and a reader writes + # code to. When the transcript disagrees with the envelope, the + # transcript is what the route actually returned, so the claim comes + # off rather than the example. + if not _is_envelope(example): + success = {'example': example} + description = ('Success. This route does NOT return the standard ' + 'envelope; the example is a transcript of a real ' + 'response.') responses = { '200': { - 'description': 'Success. Body is the success_response envelope: ' - '{status, data, meta}.', - 'content': {'application/json': { - '$ref': '#/components/schemas/SuccessEnvelope'}}, + 'description': description, + 'content': {'application/json': success}, }, 'default': { 'description': 'Error. Body is the error envelope; the code and ' @@ -256,6 +324,338 @@ def _summary(text, fallback): return (head[:cut] if cut > 40 else head).rstrip(' ,;:.+-/&|(') + '...' +# --------------------------------------------------------------------------- +# Derived query parameters +# --------------------------------------------------------------------------- +# +# A Flask rule carries its path arguments and nothing else, so a route whose +# real interface is '?code=' or '?search=' published an operation with no query +# parameters at all - 0 of 573. A machine client built from that document could +# call those endpoints but could not filter, search or page them, and +# /api/usb/kiosk/setup, where '?code=' is the ONLY way to call it successfully, +# said nothing. +# +# So the handler is read instead. This walks the view's AST for the reads that +# name their argument as a literal - request.args.get('x'), request.args['x'], +# request.args.getlist('x') - and for the two helpers this repository reads +# arguments through, shopdb.utils.pagination.get_pagination_params and +# shopdb.utils.queryargs.intarg/intargs. +# +# THE RULE IS: A NAME THAT DOES NOT RESOLVE TO A LITERAL IS NOT EMITTED. A key +# built at runtime, or read through a variable this cannot see, produces no +# parameter rather than a guessed one. The published spec is a contract, and a +# parameter that is not really there is worse than a missing one: a reader +# believes it, sends it, and is silently ignored. Silence is recoverable. +# +# Required is derived the same way. request.args['x'] raises without the +# argument, so it is required; a .get() call cannot be - it has a fallback, even +# when that fallback is None. Nothing here promotes a parameter to required +# because it looks important. + +# The argument-reading helpers, by the tail of the call. Each maps to the +# parameters that helper reads, which is a property of the helper and not of the +# route - so it is written once here rather than re-derived per view. +# +# get_pagination_params reads all three names and int()s them, which is why they +# publish as integers while a bare request.args.get() publishes as a string: the +# query string is text, and only a handler that converts makes it anything else. +PAGINATION_PARAMS = ( + {'name': 'page', 'type': 'integer', 'required': False, 'default': 1}, + {'name': 'perpage', 'type': 'integer', 'required': False}, + # The legacy spelling, still read as a fallback by the same helper. A caller + # reading only the spec would never find it, and a caller migrating from it + # would not know it still works. + {'name': 'per_page', 'type': 'integer', 'required': False}, +) + + +def _constant_string(node, constants): + """The literal string a call argument names, or None. + + Two forms resolve: a string literal, and a module-level Name bound to one + (plugins/endpoints/api/bootstrap.py reads its query token through + QUERY_TOKEN_ARG). Anything else - an f-string, a variable from an argument, + a key built at runtime - returns None and is dropped, which is the whole + honesty rule of this section. + """ + import ast + if isinstance(node, ast.Constant) and isinstance(node.value, str): + return node.value + if isinstance(node, ast.Name): + return constants.get(node.id) + return None + + +def _wire_type(call): + """The OpenAPI type for a request.args read, from its `type=` converter. + + A query string is text. Werkzeug returns a string unless the caller passed + type=int (or float, or bool), so that keyword is the only evidence here that + the value is anything else, and without it the parameter publishes as a + string. + """ + import ast + for keyword in call.keywords: + if keyword.arg != 'type': + continue + name = getattr(keyword.value, 'id', '') + return {'int': 'integer', 'float': 'number', 'bool': 'boolean'}.get(name, 'string') + return 'string' + + +def _literal_default(node, wiretype): + """A default worth publishing, or None. + + Only literals. `request.args.get('sort', DEFAULT_SORT)` has a default this + cannot see, and publishing 'DEFAULT_SORT' as the value would be a lie in the + contract; publishing no default is merely incomplete. + + A numeric default under a string type is stringified rather than dropped, + because that is what the caller actually puts on the wire: `?page=1`. + """ + import ast + if not isinstance(node, ast.Constant) or node.value is None: + return None + value = node.value + if isinstance(value, bool): + return value if wiretype == 'boolean' else str(value).lower() + if isinstance(value, (int, float)): + return value if wiretype in ('integer', 'number') else str(value) + if isinstance(value, str): + return value + return None + + +def _args_read(call, constants): + """One parameter from a request.args.get/getlist call, or None.""" + import ast + attribute = call.func + if not isinstance(attribute, ast.Attribute): + return None + if attribute.attr not in ('get', 'getlist'): + return None + # `.args.get(...)`: the receiver is what matters, not whether it + # is spelled `request` or a handler's `request_obj` parameter. + owner = attribute.value + if not isinstance(owner, ast.Attribute) or owner.attr != 'args': + return None + if not call.args: + return None + name = _constant_string(call.args[0], constants) + if not name: + return None + if attribute.attr == 'getlist': + # Repeatable: '?id=1&id=2'. Published as an array of strings, which is + # what getlist returns without a converter. + return {'name': name, 'type': 'array', 'required': False} + wiretype = _wire_type(call) + parameter = {'name': name, 'type': wiretype, 'required': False} + fallback = None + if len(call.args) > 1: + fallback = call.args[1] + for keyword in call.keywords: + if keyword.arg == 'default': + fallback = keyword.value + if fallback is not None: + default = _literal_default(fallback, wiretype) + if default is not None: + parameter['default'] = default + return parameter + + +def _helper_read(call, constants): + """Parameters from one call to an argument-reading helper.""" + import ast + tail = call.func.attr if isinstance(call.func, ast.Attribute) else getattr(call.func, 'id', '') + if tail == 'get_pagination_params': + return [dict(item) for item in PAGINATION_PARAMS] + if tail == 'intarg': + if not call.args: + return [] + name = _constant_string(call.args[0], constants) + if not name: + return [] + parameter = {'name': name, 'type': 'integer', 'required': False} + fallback = call.args[1] if len(call.args) > 1 else None + for keyword in call.keywords: + if keyword.arg == 'default': + fallback = keyword.value + if fallback is not None: + default = _literal_default(fallback, 'integer') + if default is not None: + parameter['default'] = default + return [parameter] + if tail == 'intargs': + found = [] + for node in call.args: + name = _constant_string(node, constants) + if name: + found.append({'name': name, 'type': 'integer', 'required': False}) + return found + return [] + + +def _args_subscript(node, constants): + """A parameter from `request.args['x']`, which is REQUIRED. + + Werkzeug raises a 400 BadRequestKeyError when the argument is absent, so + this is the one read in the language that proves the caller must send it. + """ + import ast + owner = node.value + if not isinstance(owner, ast.Attribute) or owner.attr != 'args': + return None + name = _constant_string(node.slice, constants) + if not name: + return None + return {'name': name, 'type': 'string', 'required': True} + + +def _merge_parameter(found, parameter): + """Fold one read into the set, keeping the strongest evidence. + + The same argument is often read twice in one handler - once to test it, once + to use it - and the two reads do not always carry the same evidence. Take + required from either (one required read is proof), the more specific type, + and the first default seen. + """ + existing = found.get(parameter['name']) + if existing is None: + found[parameter['name']] = dict(parameter) + return + if parameter.get('required'): + existing['required'] = True + if existing.get('type') == 'string' and parameter.get('type') != 'string': + existing['type'] = parameter['type'] + if 'default' not in existing and 'default' in parameter: + existing['default'] = parameter['default'] + + +def _module_constants(tree): + """Module-level names bound to a string literal, for name resolution.""" + import ast + constants = {} + for node in tree.body: + if not isinstance(node, ast.Assign): + continue + if not isinstance(node.value, ast.Constant) or not isinstance(node.value.value, str): + continue + for target in node.targets: + if isinstance(target, ast.Name): + constants[target.id] = node.value.value + return constants + + +def _query_index(filename, _cache={}): + """{function name: [parameter, ...]} of derived query arguments for one file. + + Read from the source for the same reason the auth sweep is: by the time a + view is in url_map, functools.wraps has left nothing of its body to inspect. + + ast.walk over the function covers a nested def as well, which is deliberate + - a handler that reads its arguments inside an inner function still reads + them. + """ + import ast + if filename in _cache: + return _cache[filename] + index = {} + try: + with open(filename, 'r', encoding='utf-8') as handle: + tree = ast.parse(handle.read()) + except (OSError, SyntaxError): + _cache[filename] = index + return index + constants = _module_constants(tree) + for node in ast.walk(tree): + if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + continue + found = {} + for item in ast.walk(node): + if isinstance(item, ast.Call): + parameter = _args_read(item, constants) + if parameter: + _merge_parameter(found, parameter) + for parameter in _helper_read(item, constants): + _merge_parameter(found, parameter) + elif isinstance(item, ast.Subscript): + parameter = _args_subscript(item, constants) + if parameter: + _merge_parameter(found, parameter) + index[node.name] = [found[name] for name in sorted(found)] + _cache[filename] = index + return index + + +def _reads_a_json_body(view): + """True when the handler really reads a request body. + + Evidence has to come from the HANDLER, not from a transcript. The suite + sends {} to routes that ignore it, and a transcript-driven guess published + "this route reads a JSON body" on DELETE /api/endpoints/agentkey, which + never calls get_json. A client built from that would send a body to a route + that discards it, and believe the body mattered. + + Matches request.get_json(...), request.json, request.data, request.form and + request.files, on any receiver named request, anywhere in the function. + """ + import ast + import inspect + import textwrap + try: + original = inspect.unwrap(view) + source = textwrap.dedent(inspect.getsource(original)) + tree = ast.parse(source) + except (OSError, TypeError, SyntaxError, IndentationError): + # A view defined dynamically has no readable source. Silence, not a + # guess: this decides whether the published contract claims a body. + return False + for node in ast.walk(tree): + if not isinstance(node, ast.Attribute): + continue + if node.attr not in ('get_json', 'json', 'data', 'form', 'files'): + continue + base = node.value + if isinstance(base, ast.Name) and base.id == 'request': + return True + return False + + +def _query_parameters(view): + """Derived query parameters for one url_map view, sorted by name.""" + import inspect + original = inspect.unwrap(view) + filename = inspect.getsourcefile(original) or '' + return _query_index(filename).get(original.__name__, []) + + +def _query_parameter_objects(entry): + """The inventory's derived `queryparams` as OpenAPI parameter objects. + + Defensive about the key being absent: an inventory written before this + existed, or one hand-trimmed, still generates a spec. + """ + parameters = [] + for parameter in entry.get('queryparams') or []: + name = parameter.get('name') + if not name: + continue + wiretype = parameter.get('type') or 'string' + if wiretype == 'array': + schema = {'type': 'array', 'items': {'type': 'string'}} + else: + schema = {'type': wiretype} + if 'default' in parameter: + schema['default'] = parameter['default'] + parameters.append({ + 'name': name, + 'in': 'query', + 'required': bool(parameter.get('required')), + 'schema': schema, + }) + return parameters + + def build(surfaces): paths = {} tags = [] @@ -282,13 +682,20 @@ def build(surfaces): if e.get('example'): desc.append('\n\n**Example:**\n```\n' + e['example'] + '\n```') security = _security(e.get('auth')) - parameters = _path_parameters(e.get('path')) + pathparameters = _path_parameters(e.get('path')) + # Path first, then query, so a reader sees the URL in the order + # they type it. + parameters = pathparameters + _query_parameter_objects(e) operation = { 'tags': [s['surface']], 'summary': _summary(e.get('purpose'), path), 'description': ''.join(desc), 'security': security, - 'responses': _responses(security, parameters), + # Only PATH parameters decide whether 404 is documented. A + # query filter that matches nothing is an empty list and a + # 200, not a missing record. + 'responses': _responses(security, pathparameters, + e.get('responseexample')), } if parameters: operation['parameters'] = parameters @@ -747,6 +1154,18 @@ def _existing_prose(): return prose +def _write_inventory(document): + """One writer, because --refresh and --harvest both rewrite this file. + + Same indent and same trailing newline from both, so a harvest that changes + nothing changes no bytes and the diff of a regenerated inventory is only + what actually moved. + """ + with open(INVENTORY, 'w', encoding='utf-8') as handle: + json.dump(document, handle, indent=2) + handle.write('\n') + + def _inventory_app(): """The app, with every bundled plugin enabled. @@ -816,6 +1235,20 @@ def refresh_inventory(): 'purpose': entry.get('purpose') or _seed_purpose(view, endpoint), 'example': entry.get('example') or _seed_example(path, methods, auth), }) + # DERIVED, like auth: rewritten outright rather than preserved. A query + # argument the handler no longer reads has to leave the contract, and a + # hand-written one has nothing holding it true. + derived = _query_parameters(view) + if derived: + entry['queryparams'] = derived + else: + entry.pop('queryparams', None) + # Same rule: derived, not preserved. A handler that stops reading a body + # must stop advertising one. + if _reads_a_json_body(view): + entry['readsbody'] = True + else: + entry.pop('readsbody', None) # Fixed key order, so a refresh that changes nothing changes no bytes. ordered = {key: entry[key] for key in ('method', 'path', 'auth', 'params', 'purpose', 'example')} @@ -825,9 +1258,7 @@ def refresh_inventory(): document = [{'surface': name, 'endpoints': entries} for name, entries in sorted(surfaces.items())] - with open(INVENTORY, 'w', encoding='utf-8') as handle: - json.dump(document, handle, indent=2) - handle.write('\n') + _write_inventory(document) total = sum(len(surface['endpoints']) for surface in document) print('refreshed %s: %d entries in %d surfaces (%d new)' @@ -841,11 +1272,488 @@ def refresh_inventory(): print(' review %s' % line) -def main(): - if '--refresh' in sys.argv[1:]: - refresh_inventory() +# --------------------------------------------------------------------------- +# Harvested examples, and the redaction that has to happen first +# --------------------------------------------------------------------------- +# +# 0 of 573 operations carried a machine-readable example, which is the single +# thing people mean when they ask for usable API documentation. Writing 573 of +# them by hand is not going to happen, and inventing them is forbidden: an +# example body that nobody ever sent is a lie in a contract that a generated +# client will send verbatim. +# +# The examples already exist. The test suite posts real bodies at the real app +# and asserts the real responses. So --harvest runs pytest with this file +# loaded as a plugin, records what the test client actually sent and got back, +# and folds it into the inventory. Nothing is composed here; every example is a +# transcript. +# +# Harvesting is OPTIONAL and RE-RUNNABLE. gen_openapi.py generates a spec with +# no harvest data at all (that is the normal case in a published checkout, which +# has no tests), and a second harvest overwrites the first rather than +# accumulating. + +HARVEST_DIR_VAR = 'SHOPDB_OPENAPI_HARVEST_DIR' + +# The keys and values that never reach the published spec. +# +# THIS FILE IS SERVED ON PRODUCTION AND PUBLISHED TO A WIKI. A harvested body is +# whatever a test happened to build, and tests are written with real-looking +# material: a login body carries a password, an agent body carries its token, a +# kiosk body carries a badge number, an endpoint body carries a hostname and an +# address. None of that is dangerous in a test file, and all of it is dangerous +# on a public page - a reader cannot tell a fixture from a fact, and neither can +# a scraper. +# +# Matched on the KEY first, because the key is the reliable signal: a field +# called 'passwordhash' is a credential whatever it happens to contain today, +# including when a fixture leaves it empty. Then on the VALUE, for the material +# that carries no name - an address in a free-text field, a JWT in a list. +SECRET_KEY_PATTERN = re.compile( + r'pass|pwd|secret|token|api[-_ ]?key|privatekey|credential|authoriz' + r'|displayprefix' + r'|cookie|salt|signature|sessionid|\botp\b|passphrase|hash', re.IGNORECASE) + +# A person. Not a secret, but not ours to publish either: these pages are read +# by other sites and by the public, and a name or a badge number identifies +# somebody who never agreed to appear in API documentation. +PERSON_KEY_PATTERN = re.compile( + r'username|email|firstname|lastname|fullname|displayname|badge|employee' + r'|createdby|updatedby|modifiedby|assignedto|checkedoutby|reservedby' + r'|requestedby|approvedby|\bowner|contactname|phone', re.IGNORECASE) + +# A machine. ADR-015: one plant's server name, its FQDN or its internal networks +# are neither ours to publish nor meaningful to any other site reading this. +HOST_KEY_PATTERN = re.compile( + r'hostname|fqdn|ipaddress|\bip\b|macaddress|serialnumber|servicetag' + r'|computername|machinename|sharepath|uncpath|netbios', re.IGNORECASE) + +# Searched, not matched: an address or a token embedded in a free-text field is +# as published as one that is the whole value, and a sentence that contains one +# is scrubbed entire rather than partially. +# A per-request value, normalised rather than published as recorded. +# +# WHY. A transcript carries the instant it was taken and the id of the request +# that produced it, and both are different on every run. Published verbatim, +# re-harvesting rewrote 313 of 320 examples with no change of meaning - a diff +# nobody can review, over a file that is committed and published. An example +# exists to show a reader the FORMAT of a field, not the moment one call +# happened, so the format is kept exactly and the value is pinned. +ISO_PATTERN = re.compile( + r'^(\d{4})-(\d{2})-(\d{2})([T ])(\d{2}):(\d{2}):(\d{2})' + r'(\.\d+)?(Z|[+-]\d{2}:?\d{2})?$') +DATE_PATTERN = re.compile(r'^\d{4}-\d{2}-\d{2}$') +STABLE_DATE = '2026-01-01' +STABLE_TIME = '00:00:00' +REQUEST_ID_KEYS = ('requestid', 'request_id', 'correlationid') +STABLE_REQUEST_ID = '00000000' + +EMAIL_PATTERN = re.compile(r'[^@\s]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}') +JWT_PATTERN = re.compile(r'\beyJ[A-Za-z0-9_-]{8,}') +HEX_PATTERN = re.compile(r'^[0-9a-fA-F]{32,}$') +# An opaque handle: long, no spaces, and mixing letters with digits. A sentence +# does not look like this and a slug is not this long. +# +# ':' and '/' are NOT in the class, deliberately. With them, every example URL +# in the suite ('http://localhost:5001/api/assets') matched and the examples lost +# the one string in them a reader most wants to see. A base64 token containing +# '/' therefore slips this rule - it is caught by its key instead, which is the +# reliable half of this anyway. +OPAQUE_PATTERN = re.compile(r'^(?=[^\s]*\d)(?=[^\s]*[A-Za-z])[A-Za-z0-9_.+=-]{24,}$') +# A credential riding in a URL's query string. This is the hole the key layer +# and the opaque layer BOTH miss by construction: the key is 'scripturl' or +# 'url', which names no secret, and OPAQUE_PATTERN excludes ':' and '/' so the +# surrounding URL never matches. A harvest published a real bootstrap token and +# a real kiosk setup code that way, in a file that is served on prod and copied +# to a wiki. The whole URL must not be dropped - it is the most useful string in +# the example - so only the parameter VALUE is scrubbed. +URL_CREDENTIAL_PATTERN = re.compile( + r'(?i)\b(t|token|code|key|apikey|api_key|secret|sig|signature|password|pw|' + r'auth|access_token|refresh_token)=([^&\s"\']+)') +IPV4_PATTERN = re.compile(r'\b(?:\d{1,3}\.){3}\d{1,3}\b') +UNC_PATTERN = re.compile(r'^\\\\[^\\]+\\') +# A path off the machine the harvest ran on. A pytest tmpdir is a different +# string on every run, and a developer's home directory is the exact material +# tests/test_docs_publishable.py exists to keep out of published pages. +LOCAL_PATH_PATTERN = re.compile(r'(^|\s)/(tmp|home|Users|var/folders)/|pytest-of-') + +# Addresses that name nothing: a loopback or a documentation host is safe to +# publish and is more useful in an example than a placeholder. +PUBLISHABLE_HOSTS = ('127.0.0.1', '0.0.0.0', 'localhost', 'example.com', + 'example.local', 'test.local') + +# How big an example may get. The spec is downloaded by every reader and parsed +# by every generated client, so a 400-row list response in one operation costs +# everybody. Three rows show the shape, which is the entire job of an example. +EXAMPLE_MAX_CHARS = 2000 +EXAMPLE_MAX_ITEMS = 3 +EXAMPLE_MAX_DEPTH = 8 +EXAMPLE_MAX_STRING = 200 + + +def _stable_timestamp(text): + """The same timestamp with its value pinned and its shape untouched. + + Fractional digits and the zone suffix are preserved exactly, because that is + the part a reader is reading it for - '2026-09-18T18:30:48.498252Z' and + '2026-09-18 18:30:48' are two different contracts. + """ + # A bare date too: a fixture that calls date.today() churns exactly like a + # timestamp does, and the shape is all an example was showing. + if DATE_PATTERN.match(text): + return STABLE_DATE + match = ISO_PATTERN.match(text) + if not match: + return None + fraction = '' + if match.group(8): + fraction = '.' + '0' * len(match.group(8)[1:]) + return (STABLE_DATE + match.group(4) + STABLE_TIME + fraction + + (match.group(9) or '')) + + +def _redacted_string(value): + """The marker for a string value that must not be published, or None. + + Returns None when the value is safe, so the caller can tell "checked and + fine" from "scrubbed". + """ + text = value.strip() + if not text: + return None + if EMAIL_PATTERN.search(text): + return '' + if JWT_PATTERN.search(text): + return '' + if UNC_PATTERN.match(text): + return '' + if LOCAL_PATH_PATTERN.search(text): + return '' + if HEX_PATTERN.match(text): + return '' + for found in IPV4_PATTERN.findall(text): + if found not in PUBLISHABLE_HOSTS: + return '' + if OPAQUE_PATTERN.match(text): + return '' + scrubbed = URL_CREDENTIAL_PATTERN.sub( + lambda match: '%s=' % match.group(1), text) + if scrubbed != text: + return scrubbed + return None + + +def redact(value, key='', depth=0): + """Scrub secrets, people and machines out of a harvested body. + + Key-driven first, value-driven second, and applied to every level - a token + two objects down is as published as one at the top. The marker names WHICH + class was matched, so a reader can see that the field exists and is not + simply empty, without learning what was in it. + + Deliberately conservative in the direction of over-redacting. A redacted + example is still a shape a client can copy; a published badge number cannot + be un-published. + """ + name = (key or '').lower() + if depth > EXAMPLE_MAX_DEPTH: + return '' + if isinstance(value, dict): + return {str(itemkey): redact(itemvalue, str(itemkey), depth + 1) + for itemkey, itemvalue in value.items()} + if isinstance(value, list): + # Only the first few rows survive, and the cut is recorded rather than + # silent: a reader who sees three assets should not conclude the list + # endpoint returns three. + trimmed = [redact(item, name, depth + 1) for item in value[:EXAMPLE_MAX_ITEMS]] + if len(value) > EXAMPLE_MAX_ITEMS: + trimmed.append('' + % (len(value) - EXAMPLE_MAX_ITEMS)) + return trimmed + # A NUMBER under a key ending in 'id' is a row identifier, not a credential. + # Without this, 'tokenid': 3 published as and the example + # for every token route lost the one field a caller needs to address the + # row. The exemption is deliberately narrow - numeric only, 'id' only, and + # it does not apply to the person or host rules below, where a badge number + # is a number and still nobody's business. + numericid = (isinstance(value, int) and not isinstance(value, bool) + and name.endswith('id')) + if name and not numericid and SECRET_KEY_PATTERN.search(name): + return '' + if name and PERSON_KEY_PATTERN.search(name): + return '' + if name and HOST_KEY_PATTERN.search(name): + return '' + if name in REQUEST_ID_KEYS: + return STABLE_REQUEST_ID + if isinstance(value, str): + marker = _redacted_string(value) + if marker: + return marker + pinned = _stable_timestamp(value) + if pinned is not None: + return pinned + if len(value) > EXAMPLE_MAX_STRING: + return value[:EXAMPLE_MAX_STRING] + '...' + return value + return value + + +def _example_fits(value): + """Whether a redacted example is small enough to publish.""" + try: + return len(json.dumps(value)) <= EXAMPLE_MAX_CHARS + except (TypeError, ValueError): + return False + + +def _best_example(samples): + """One example out of every sample recorded for an operation. + + Deterministic, because the suite runs in parallel and the order samples + arrive in is not stable: sort by size descending then by text, and take the + first that fits. Largest-that-fits is the most informative, and the text + tiebreak means two runs of the same suite produce the same spec - which is + what makes a regenerated docs/openapi.json reviewable as a diff. + """ + candidates = [] + for sample in samples: + scrubbed = redact(sample) + if not _example_fits(scrubbed): + continue + text = json.dumps(scrubbed, sort_keys=True) + candidates.append((-len(text), text, scrubbed)) + if not candidates: + return None + candidates.sort(key=lambda row: (row[0], row[1])) + return candidates[0][2] + + +# --------------------------------------------------------------------------- +# The pytest plugin half: loaded with `-p gen_openapi` by --harvest +# --------------------------------------------------------------------------- +# +# Flask.full_dispatch_request is patched rather than an after_request handler +# registered, because the app under test is built by the suite's own session +# fixture and there is no reliable moment to reach into it - the conftest +# imports create_app at module import, before any hook here could wrap it. +# Patching the class catches every app in the process however and whenever it +# was created. +# +# Recorded to one file per process, appended a line at a time: the suite runs +# under xdist, so several workers record at once, and a crash in one of them +# must not cost the others their samples. + +_HARVEST_STATE = {'original': None, 'path': None} + + +def _harvest_record(flaskrequest, response): + """Append one request/response transcript, if it is one worth keeping.""" + rule = getattr(flaskrequest, 'url_rule', None) + if rule is None or not rule.rule.startswith(API_PREFIX): + return + status = getattr(response, 'status_code', 0) + # A body that produced a 400 is an example of getting it wrong, and a 500 is + # an example of nothing. Only a call that worked describes the contract. + if status < 200 or status >= 300: + return + sent = None + try: + if flaskrequest.mimetype == 'application/json': + sent = flaskrequest.get_json(silent=True) + except Exception: + sent = None + got = None + try: + if (response.mimetype == 'application/json' + and not getattr(response, 'direct_passthrough', False) + and not getattr(response, 'is_streamed', False)): + got = json.loads(response.get_data(as_text=True)) + except Exception: + got = None + if sent is None and got is None: + return + line = json.dumps({ + 'method': flaskrequest.method, + 'rule': rule.rule, + 'request': sent, + 'response': got, + }) + try: + with open(_HARVEST_STATE['path'], 'a', encoding='utf-8') as handle: + handle.write(line + '\n') + except OSError: + pass + + +def pytest_configure(config): + """Wrap Flask's dispatch so every test-client call is transcribed. + + Inert unless --harvest set the directory, so loading this file as a plugin + in an ordinary run changes nothing. + """ + outdir = os.environ.get(HARVEST_DIR_VAR) + if not outdir or _HARVEST_STATE['original'] is not None: + return + import flask + original = flask.Flask.full_dispatch_request + _HARVEST_STATE['original'] = original + _HARVEST_STATE['path'] = os.path.join(outdir, 'samples-%d.jsonl' % os.getpid()) + + def dispatch(self): + response = original(self) + # A fault in the recorder must never change what the suite reports. The + # harvest is documentation; the tests are the product. + try: + _harvest_record(flask.request, response) + except Exception: + pass + return response + + flask.Flask.full_dispatch_request = dispatch + + +def pytest_unconfigure(config): + if _HARVEST_STATE['original'] is None: + return + import flask + flask.Flask.full_dispatch_request = _HARVEST_STATE['original'] + _HARVEST_STATE['original'] = None + + +def _read_samples(outdir): + """{(METHOD, rule): {'request': [...], 'response': [...]}} from a harvest run.""" + collected = {} + if not os.path.isdir(outdir): + return collected + for name in sorted(os.listdir(outdir)): + if not name.endswith('.jsonl'): + continue + with open(os.path.join(outdir, name), 'r', encoding='utf-8') as handle: + for line in handle: + line = line.strip() + if not line: + continue + try: + row = json.loads(line) + except ValueError: + continue + key = (row.get('method', '').upper(), row.get('rule', '')) + bucket = collected.setdefault(key, {'request': [], 'response': []}) + if row.get('request') is not None: + bucket['request'].append(row['request']) + if row.get('response') is not None: + bucket['response'].append(row['response']) + return collected + + +def fold_examples(collected): + """Write harvested examples into the inventory. Returns (request, response) counts. + + The inventory is the input to the generator, so this is where they belong - + it keeps the spec a pure function of the inventory, and it means a harvest + survives in the committed file for a reader who never runs the suite. + + ADDITIVE, deliberately. Only a route this run actually exercised is + rewritten; an entry the run never reached keeps the example it has. Two + reasons, and the second is the important one: + + A targeted harvest has to be targeted. `--harvest tests/test_core` would + otherwise strip the examples off every route those tests do not touch, + which is most of them - the narrow command would do the most damage. + + The suite does not exercise the same set twice. Run under xdist against a + shared instance directory, two full runs of the same tree reached 311 and + 327 operations. Clearing on absence turned that into a churn of examples + appearing and vanishing in a committed, published file, with nothing + wrong in the tree. + + An example that has gone stale is still a transcript of a response this app + really gave. A route that is gone entirely takes its entry with it, which + tests/test_api_inventory_parity.py already enforces. + """ with open(INVENTORY, 'r', encoding='utf-8') as handle: surfaces = json.load(handle) + requests = 0 + responses = 0 + for surface in surfaces: + for entry in surface.get('endpoints', []): + path = entry.get('path') or '' + sent = [] + got = [] + for method in re.split(r'[/,|]', entry.get('method') or 'GET'): + bucket = collected.get((method.strip().upper(), path)) + if bucket: + sent.extend(bucket['request']) + got.extend(bucket['response']) + example = _best_example(sent) + if example is not None: + entry['requestexample'] = example + example = _best_example(got) + if example is not None: + entry['responseexample'] = example + requests += 1 if 'requestexample' in entry else 0 + responses += 1 if 'responseexample' in entry else 0 + _write_inventory(surfaces) + return requests, responses + + +def harvest(pytestargs): + """Run the suite with the recorder attached, then fold what it saw in.""" + import subprocess + import tempfile + outdir = tempfile.mkdtemp(prefix='shopdb-openapi-harvest-') + environment = dict(os.environ) + environment[HARVEST_DIR_VAR] = outdir + # This file is the plugin. Putting scripts/ on the path is what makes + # `-p gen_openapi` resolvable, in the workers as well as the controller. + environment['PYTHONPATH'] = os.pathsep.join( + [HERE] + ([environment['PYTHONPATH']] if environment.get('PYTHONPATH') else [])) + command = [sys.executable, '-m', 'pytest', '-p', 'gen_openapi', '-q'] + command.extend(pytestargs or ['tests']) + print('harvesting examples: ' + ' '.join(command)) + # The suite's own exit status is not this command's: a red test still + # produced real transcripts for every route it did reach, and refusing to + # record them would make the documentation hostage to an unrelated failure. + subprocess.call(command, cwd=REPO, env=environment) + collected = _read_samples(outdir) + requests, responses = fold_examples(collected) + print('harvested %d operation(s); inventory now carries %d request ' + 'example(s) and %d response example(s)' + % (len(collected), requests, responses)) + + +EXAMPLE_FIELDS = ('requestexample', 'responseexample') + + +def _scrub_stored_examples(surfaces): + """Re-redact every example the inventory carries, before it is published.""" + for surface in surfaces: + for entry in surface.get('endpoints', []): + for field in EXAMPLE_FIELDS: + if field in entry and entry[field] is not None: + entry[field] = redact(entry[field], field) + return surfaces + + +def main(): + arguments = sys.argv[1:] + if '--refresh' in arguments: + refresh_inventory() + if '--harvest' in arguments: + # Everything after the flag is pytest's, so a targeted harvest is + # `--harvest tests/test_core/test_assets.py` and needs no second flag. + harvest(arguments[arguments.index('--harvest') + 1:]) + with open(INVENTORY, 'r', encoding='utf-8') as handle: + surfaces = json.load(handle) + # SCRUB ON THE WAY OUT AS WELL AS ON THE WAY IN. Harvested examples are + # stored in the inventory and a refresh preserves them, so a scrubber fixed + # after a harvest protects only future harvests - the stored copy keeps + # publishing whatever the old rules missed. On 2026-09-18 that was a live + # bootstrap token and a live kiosk setup code, both hidden in a URL. Running + # redaction here means the published file cannot carry a secret that the + # CURRENT rules would catch, whatever is sitting in the inventory. + surfaces = _scrub_stored_examples(surfaces) spec = build(surfaces) with open(OUT, 'w', encoding='utf-8') as handle: json.dump(spec, handle, indent=1) diff --git a/tests/test_api_inventory_parity.py b/tests/test_api_inventory_parity.py index f8637fc1..23df9351 100644 --- a/tests/test_api_inventory_parity.py +++ b/tests/test_api_inventory_parity.py @@ -183,3 +183,63 @@ def test_documented_auth_is_what_the_code_enforces(): 'inventory is derived, so fix the code or teach the classifier in ' 'scripts/gen_openapi.py, then re-run it with --refresh:\n %s' % (len(disagreements), '\n '.join(disagreements[:15]))) + + +# --------------------------------------------------------------------------- +# The third derived field: query parameters +# --------------------------------------------------------------------------- +# +# A Flask rule carries its path arguments and nothing else, so a route whose +# real interface is '?code=' or '?search=' documented no parameters at all - +# /api/usb/kiosk/setup among them, where '?code=' is the ONLY way to call it +# successfully. gen_openapi.py now reads them out of each view's AST and writes +# them into the inventory as `queryparams`. +# +# That field is DERIVED, like auth, and gated the same way and for the same +# reason: a refresh only rewrites, so without a comparison a hand-edit or a +# stale file publishes a query contract the handler does not implement. A +# parameter that is not really there is the worst kind of documentation defect, +# because a reader believes it, sends it, and is silently ignored. + + +def derived_queryparams(): + """{(METHOD, path): [parameter, ...]} as the sweep reads the code now.""" + generator = _generator() + app = create_app('testing') + derived = {} + for rule, methods, endpoint, view in generator._served_routes(app): + parameters = generator._query_parameters(view) + for method in methods: + derived[(method, normalise(rule))] = parameters + return derived + + +def documented_queryparams(): + """{(METHOD, path): [parameter, ...]} as the inventory has it committed.""" + documented = {} + for surface in json.loads(INVENTORY.read_text()): + for entry in surface.get('endpoints', []): + path = normalise(entry.get('path') or '') + for method in re.split(r'[/,|]', entry.get('method') or 'GET'): + method = method.strip().upper() + if method: + documented[(method, path)] = entry.get('queryparams') or [] + return documented + + +def test_documented_query_parameters_are_what_the_handler_reads(): + derived = derived_queryparams() + documented = documented_queryparams() + disagreements = [] + for key in sorted(set(derived) & set(documented)): + if derived[key] != documented[key]: + disagreements.append( + '%s %s\n documented: %s\n read: %s' + % (key[0], key[1], + [item['name'] for item in documented[key]], + [item['name'] for item in derived[key]])) + assert not disagreements, ( + '%d route(s) document query parameters that are not what the handler ' + 'reads. The field is derived, so regenerate with ' + 'scripts/gen_openapi.py --refresh:\n %s' + % (len(disagreements), '\n '.join(disagreements[:15]))) diff --git a/tests/test_openapi_examples.py b/tests/test_openapi_examples.py new file mode 100644 index 00000000..c0e1eeb6 --- /dev/null +++ b/tests/test_openapi_examples.py @@ -0,0 +1,637 @@ +"""The two derived halves of the published spec: query parameters and examples. + +docs/openapi.json is served at /api/docs on production AND published to the +wiki, and it is what the MCP server builds its tools from. Two things were +missing from every operation in it, and both are now derived rather than +written: + + QUERY PARAMETERS. A Flask rule carries its path arguments and nothing else, + so 0 of 573 operations published a query parameter - including + /api/usb/kiosk/setup, where '?code=' is the only way to call it successfully. + scripts/gen_openapi.py now reads them out of each view's AST. + + EXAMPLES. 0 of 573 carried a machine-readable example. They are harvested + from the test suite, which already sends real bodies and asserts real + responses. + +Harvesting is where this module earns its place. A harvested body is whatever a +test happened to build, and tests are written with real-looking material - +passwords, tokens, badge numbers, hostnames, addresses. None of that matters in +a test file and all of it matters on a public page, so redaction runs before +anything reaches the spec. `redact` is tested here directly, and the generated +spec is swept for the same material, because a scrubber nobody checks is a +scrubber that stops working quietly. + +The other property tested here is that NOTHING IS INVENTED. A query argument +whose name the generator cannot resolve to a literal is not published at all: +an argument that is not really there is worse than a missing one, because a +reader believes it, sends it and is silently ignored. +""" + +import importlib.util +import json +import textwrap +from pathlib import Path + +import pytest +import re + +REPO = Path(__file__).resolve().parents[1] +SPEC = REPO / 'docs' / 'openapi.json' + + +def _generator(): + """scripts/gen_openapi.py, loaded by path. + + It is a script, not a package module. Loading it runs its imports and + nothing else - it builds no app and reads no database until it is asked to. + """ + path = REPO / 'scripts' / 'gen_openapi.py' + spec = importlib.util.spec_from_file_location('gen_openapi_examples', path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +@pytest.fixture(scope='module') +def generator(): + return _generator() + + +@pytest.fixture(scope='module') +def spec(): + if not SPEC.is_file(): + pytest.skip('no generated spec in this checkout') + return json.loads(SPEC.read_text()) + + +def operations(spec): + for path, item in spec['paths'].items(): + for verb, operation in item.items(): + yield path, verb, operation + + +def examples(spec): + """Every published example, as (where, value).""" + for path, verb, operation in operations(spec): + body = operation.get('requestBody') or {} + media = (body.get('content') or {}).get('application/json') or {} + if 'example' in media: + yield '%s %s request' % (verb.upper(), path), media['example'] + success = ((operation.get('responses') or {}).get('200') or {}) + media = (success.get('content') or {}).get('application/json') or {} + if 'example' in media: + yield '%s %s response' % (verb.upper(), path), media['example'] + + +# --------------------------------------------------------------------------- +# redact +# --------------------------------------------------------------------------- + + +def test_a_credential_is_redacted_by_its_key(generator): + """The key is the reliable signal, whatever the value happens to be today. + + A fixture that leaves passwordhash empty must not teach the scrubber that + passwordhash is safe. + """ + scrubbed = generator.redact({ + 'password': 'hunter2', + 'passwordhash': '', + 'apikey': 'abc', + 'refresh_token': 'abc', + 'X-API-Key': 'abc', + 'signature': 'abc', + }) + assert set(scrubbed.values()) == {''} + + +def test_a_person_and_a_machine_are_redacted_by_their_key(generator): + """Not secrets, and not ours to publish either. ADR-015 for the machine + half: one plant's server name is meaningless to every other site reading + these pages.""" + scrubbed = generator.redact({ + 'username': 'jsmith', + 'badge': '204411', + 'checkedoutby': 'J Smith', + 'hostname': 'WJSF0123', + 'fqdn': 'wjsf0123.example.net', + 'ipaddress': '10.4.2.19', + 'servicetag': 'H3K9QP2', + }) + assert scrubbed['username'] == '' + assert scrubbed['badge'] == '' + assert scrubbed['checkedoutby'] == '' + assert scrubbed['hostname'] == '' + assert scrubbed['fqdn'] == '' + assert scrubbed['ipaddress'] == '' + assert scrubbed['servicetag'] == '' + + +def test_a_credential_with_no_telling_key_is_redacted_by_its_value(generator): + """The half the key rules cannot reach: material in a free-text field, or + in a list, where nothing names it.""" + scrubbed = generator.redact({ + 'note': 'mail bob.smith@example.com when it lands', + 'detail': 'bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.aaaa.bbbb', + 'path': '\\\\fileserver\\deploy\\agent.msi', + 'where': 'reported from 10.11.12.13', + 'digest': 'd41d8cd98f00b204e9800998ecf8427e', + 'bag': ['carol@example.com'], + }) + assert scrubbed['note'] == '' + assert scrubbed['detail'] == '' + assert scrubbed['path'] == '' + assert scrubbed['where'] == '' + assert scrubbed['digest'] == '' + assert scrubbed['bag'] == [''] + + +def test_ordinary_material_survives(generator): + """Over-redaction has a cost too: an example scrubbed to nothing shows a + reader nothing. What is safe stays.""" + payload = { + 'assetnumber': 'A-10024', + 'model': 'Dell Latitude 5420', + 'isactive': True, + 'quantity': 12, + 'status': 'In Service', + 'url': 'http://localhost:5001/api/assets', + } + assert generator.redact(payload) == payload + + +def test_a_numeric_id_is_not_a_credential(generator): + """'tokenid': 3 is the row number, not the token. + + Redacting it cost every token route the one field a caller needs to address + the row. The exemption is numeric-only, so a string session id is still + scrubbed. + """ + scrubbed = generator.redact({'tokenid': 3, 'apikeyid': 7, + 'sessionid': 'a-long-opaque-string-value'}) + assert scrubbed['tokenid'] == 3 + assert scrubbed['apikeyid'] == 7 + assert scrubbed['sessionid'] == '' + + +def test_redaction_reaches_every_level(generator): + """A token two objects down is as published as one at the top.""" + scrubbed = generator.redact( + {'data': {'items': [{'inner': {'password': 'hunter2'}}]}}) + assert scrubbed['data']['items'][0]['inner']['password'] == '' + + +def test_redaction_is_idempotent(generator): + """A harvest runs again over a spec that already carries markers, and the + markers must not be re-scrubbed into something else.""" + once = generator.redact({'password': 'hunter2', 'model': 'Dell 5420'}) + assert generator.redact(once) == once + + +def test_a_long_list_is_trimmed_and_says_so(generator): + """Three rows show the shape, which is the whole job of an example - but a + reader must not conclude the endpoint returns three.""" + scrubbed = generator.redact({'data': list(range(50))}) + assert scrubbed['data'][:3] == [0, 1, 2] + assert 'more' in scrubbed['data'][-1] + + +def test_a_cyclic_depth_is_bounded(generator): + """Harvested bodies come from a running app, not from this file. A deeply + nested one must not take the generator down with it.""" + deep = current = {} + for _ in range(50): + current['child'] = {} + current = current['child'] + generator.redact(deep) + + +# --------------------------------------------------------------------------- +# The published spec, swept for the same material +# --------------------------------------------------------------------------- + + +# Patterns held HERE, deliberately not imported from the generator. A gate that +# checks the scrubber with the scrubber's own patterns can only ever assert that +# redact() ran: it was green while the spec published a live bootstrap token and +# a live kiosk setup code, because both hid in a URL whose key ('scripturl', +# 'url') names no secret and whose value the opaque rule excludes by design. +# These must keep catching that class even if someone edits the generator. +INDEPENDENT_URL_CREDENTIAL = re.compile( + r'(?i)[?&](t|token|code|key|apikey|api_key|secret|sig|signature|password|' + r'auth|access_token|refresh_token)=([^&\s"\'<]{8,})') +# Product token prefixes. If a new one is minted, add it here the same day. +INDEPENDENT_TOKEN_PREFIX = re.compile(r'\b(sdbb_|sdbe_|shopdb_pat_)[A-Za-z0-9_-]{8,}') + + +def _looks_high_entropy(text): + """A long mixed-case, digit-bearing run with no word structure. + + Deliberately crude: it is a backstop for a secret shape nobody has thought + of yet, not a classifier. Anything it flags is either a secret or an example + that would read better with a shorter value. + """ + for chunk in re.split(r'[\s"\'<>]+', text): + if len(chunk) < 28 or '.' in chunk or '/' in chunk: + continue + has = lambda pattern: re.search(pattern, chunk) + if has(r'[a-z]') and has(r'[A-Z]') and has(r'\d'): + return chunk + return None + + +def test_no_published_example_hides_a_credential_in_a_url(spec): + """The class the scrubber's own patterns cannot see. + + Two real credentials reached docs/openapi.json this way on 2026-09-18, in a + file served at /api/docs on production and copied to a published wiki. + """ + offenders = [] + for where, example in examples(spec): + blob = json.dumps(example) + found = INDEPENDENT_URL_CREDENTIAL.search(blob) + if found and not found.group(2).startswith(' generator.EXAMPLE_MAX_CHARS] + assert not offenders, '\n '.join(offenders[:15]) + + +def test_a_response_example_never_contradicts_the_schema_beside_it(spec): + """An example and a `$ref` that disagree are worse than no example at all. + + Nineteen routes here do not return the standard envelope and every one was + documented as if it did. A harvested transcript is evidence about which, + so the generator drops the envelope `$ref` on any operation whose real + response is a different shape. This is the gate on that: an operation + published with the envelope `$ref` and an example that is not an envelope + is the exact contradiction the harvest exists to remove. + """ + offenders = [] + for path, verb, operation in operations(spec): + media = (((operation.get('responses') or {}).get('200') or {}) + .get('content') or {}).get('application/json') or {} + if 'example' not in media: + continue + claims = 'SuccessEnvelope' in json.dumps(media.get('schema') or {}) + isenvelope = (isinstance(media['example'], dict) + and media['example'].get('status') == 'success') + if claims != isenvelope: + offenders.append('%s %s: schema says envelope=%s, example is=%s' + % (verb.upper(), path, claims, isenvelope)) + assert not offenders, '\n '.join(offenders[:15]) + + +# --------------------------------------------------------------------------- +# Derived query parameters: what is emitted, and what is deliberately not +# --------------------------------------------------------------------------- + + +def _derive(tmp_path, source): + """Run the generator's query sweep over one written-out module.""" + module = tmp_path / 'sample_views.py' + module.write_text(textwrap.dedent(source)) + return _generator()._query_index(str(module)) + + +def test_a_literal_query_read_is_emitted(tmp_path): + index = _derive(tmp_path, ''' + def view(): + search = request.args.get('search') + active = request.args.get('active', 'true') + days = request.args.get('days', 30, type=int) + tags = request.args.getlist('tag') + page = request.args['page'] + ''') + found = {item['name']: item for item in index['view']} + assert sorted(found) == ['active', 'days', 'page', 'search', 'tag'] + assert found['search'] == {'name': 'search', 'type': 'string', 'required': False} + assert found['active']['default'] == 'true' + assert found['days'] == {'name': 'days', 'type': 'integer', + 'required': False, 'default': 30} + assert found['tag']['type'] == 'array' + # request.args['x'] raises without the argument. It is the one read in the + # language that proves the caller must send it. + assert found['page']['required'] is True + + +def test_a_name_that_is_not_a_literal_is_not_published(tmp_path): + """The honesty rule, and the reason this is safe to derive at all. + + A key built at runtime cannot be resolved, and a parameter that is not + really there is worse than a missing one: a reader believes it, sends it, + and is silently ignored. A module-level constant DOES resolve, because it + is a literal with a name. + """ + index = _derive(tmp_path, ''' + TOKEN_ARG = 'bootstraptoken' + + def view(field): + built = request.args.get('filter_' + field) + indirect = request.args.get(field) + formatted = request.args.get(f'{field}id') + named = request.args.get(TOKEN_ARG) + ''') + assert [item['name'] for item in index['view']] == ['bootstraptoken'] + + +def test_the_repos_own_argument_helpers_are_read(tmp_path): + """get_pagination_params and intarg/intargs read arguments on a handler's + behalf, so a sweep that only looks for request.args misses the paging + contract of every list route in the product.""" + index = _derive(tmp_path, ''' + def view(): + page, perpage = get_pagination_params(request) + typeid = intarg('typeid') + since = intarg('days', 30) + rest = intargs('locationid', 'businessunitid') + ''') + found = {item['name']: item for item in index['view']} + assert sorted(found) == ['businessunitid', 'days', 'locationid', 'page', + 'per_page', 'perpage', 'typeid'] + assert found['page'] == {'name': 'page', 'type': 'integer', + 'required': False, 'default': 1} + assert found['days']['default'] == 30 + assert found['typeid']['type'] == 'integer' + + +def test_a_view_that_reads_nothing_publishes_nothing(tmp_path): + index = _derive(tmp_path, ''' + def view(asset_id): + return success_response({'assetid': asset_id}) + ''') + assert index['view'] == [] + + +# --------------------------------------------------------------------------- +# The generator without any of this +# --------------------------------------------------------------------------- + + +def test_a_spec_generates_with_no_harvest_and_no_query_parameters(generator): + """Both halves are OPTIONAL. A published checkout has no tests to harvest + from, and an inventory written before this existed has no `queryparams`. A + generator that needs either is a generator that cannot be run where it + matters.""" + built = generator.build([{ + 'surface': 'core-assets', + 'endpoints': [{ + 'method': 'GET|POST', + 'path': '/api/assets', + 'auth': 'permission:assets.view', + 'params': 'not documented', + 'purpose': 'List assets.', + 'example': 'curl http://localhost:5001/api/assets', + }], + }]) + operation = built['paths']['/api/assets']['get'] + assert 'parameters' not in operation + media = built['paths']['/api/assets']['post']['requestBody']['content'] + assert 'example' not in media['application/json'] + success = operation['responses']['200']['content']['application/json'] + assert 'example' not in success + assert success['schema'] == {'$ref': '#/components/schemas/SuccessEnvelope'} + + +def test_a_query_parameter_survives_into_the_spec(generator): + """The other direction: an inventory entry that has them publishes them.""" + built = generator.build([{ + 'surface': 'plugin-usb', + 'endpoints': [{ + 'method': 'GET', + 'path': '/api/usb/kiosk/setup', + 'auth': 'setup-code (single-use kiosk setup code in ?code=)', + 'params': 'not documented', + 'purpose': 'Trade a setup code for this browser kiosk cookie.', + 'example': 'curl http://localhost:5001/api/usb/kiosk/setup', + 'queryparams': [{'name': 'code', 'type': 'string', 'required': False}], + 'responseexample': {'status': 'success', 'data': None}, + }], + }]) + operation = built['paths']['/api/usb/kiosk/setup']['get'] + assert operation['parameters'] == [{ + 'name': 'code', 'in': 'query', 'required': False, + 'schema': {'type': 'string'}, + }] + success = operation['responses']['200']['content']['application/json'] + assert success['example'] == {'status': 'success', 'data': None} + # A query filter that matches nothing is an empty list and a 200. Only a + # path parameter can be a record that is not there. + assert '404' not in operation['responses'] + + +def test_a_per_request_value_is_pinned_but_keeps_its_shape(generator): + """A transcript carries the instant it was taken and the id of the request + that produced it, and both differ on every run. + + Published verbatim, re-harvesting rewrote 313 of 320 examples with no change + of meaning - an unreviewable diff over a committed, published file. An + example shows a reader the FORMAT of a field, not the moment one call + happened, so the format is kept exactly and the value is pinned. + """ + scrubbed = generator.redact({ + 'meta': {'timestamp': '2026-09-18T18:30:48.498252Z', + 'requestid': '859aa86b'}, + 'createddate': '2026-09-18 08:15:00', + 'expiresat': '2026-12-31T23:59:59+00:00', + 'installedon': '2026-09-18', + }) + # Microseconds, the space separator and the offset all survive: those are + # three different contracts and a reader is reading the example for which. + assert scrubbed['meta']['timestamp'] == '2026-01-01T00:00:00.000000Z' + assert scrubbed['meta']['requestid'] == '00000000' + assert scrubbed['createddate'] == '2026-01-01 00:00:00' + assert scrubbed['expiresat'] == '2026-01-01T00:00:00+00:00' + assert scrubbed['installedon'] == '2026-01-01' + + +def test_pinning_does_not_reach_ordinary_text(generator): + """Only a value that IS a timestamp. A model number that happens to contain + digits is not one.""" + payload = {'model': '5420-2026', 'note': 'due 2026-09-18 at the latest', + 'version': '1.2.3'} + assert generator.redact(payload) == payload + + +def test_a_local_path_is_not_published(generator): + """A path off the machine the harvest ran on says nothing to a reader and + is different on every run. A developer's home directory is also the exact + material tests/test_docs_publishable.py exists to keep off a public page.""" + scrubbed = generator.redact({ + 'shareroot': '/tmp/pytest-of-someone/pytest-240/test_roundtrip0', + 'checkout': '/home/someone/projects/shopdb-flask', + 'relative': 'deploy/windows/installer', + 'route': '/api/assets', + }) + assert scrubbed['shareroot'] == '' + assert scrubbed['checkout'] == '' + # A path that names no machine is the useful half, and it stays. + assert scrubbed['relative'] == 'deploy/windows/installer' + assert scrubbed['route'] == '/api/assets' + + +def test_a_harvest_adds_and_never_clears(generator, tmp_path): + """A targeted harvest has to be targeted. + + Clearing on absence meant `--harvest tests/test_core` stripped the examples + off every route those tests do not touch, which is most of them - the + narrow command would have done the most damage. It also made the committed + file churn for nothing: run under xdist against a shared instance + directory, two full runs of the same tree reached 311 and 327 operations. + """ + inventory = tmp_path / 'api-inventory.json' + inventory.write_text(json.dumps([{ + 'surface': 'core-assets', + 'endpoints': [ + {'method': 'GET', 'path': '/api/assets', + 'responseexample': {'status': 'success', 'data': ['kept']}}, + {'method': 'POST', 'path': '/api/assets'}, + ], + }])) + original = generator.INVENTORY + generator.INVENTORY = str(inventory) + try: + generator.fold_examples({ + ('POST', '/api/assets'): { + 'request': [{'assetnumber': 'A-1'}], + 'response': [{'status': 'success', 'data': {'assetid': 1}}], + }, + }) + finally: + generator.INVENTORY = original + entries = json.loads(inventory.read_text())[0]['endpoints'] + # Untouched by this harvest, and still carrying what it had. + assert entries[0]['responseexample'] == {'status': 'success', 'data': ['kept']} + assert entries[1]['requestexample'] == {'assetnumber': 'A-1'} + assert entries[1]['responseexample'] == {'status': 'success', + 'data': {'assetid': 1}} + + +def test_picking_one_example_out_of_many_is_deterministic(generator): + """The suite runs in parallel, so the order samples arrive in is not stable. + + Largest-that-fits with a text tiebreak means two runs over the same samples + produce the same spec, which is what makes a regenerated docs/openapi.json + reviewable as a diff instead of a rewrite. + """ + samples = [ + {'status': 'success', 'data': {'assetid': 1}}, + {'status': 'success', 'data': {'assetid': 2, 'assetnumber': 'A-2'}}, + {'status': 'success', 'data': None}, + ] + picked = generator._best_example(samples) + assert picked == generator._best_example(list(reversed(samples))) + assert picked['data']['assetnumber'] == 'A-2' + + +def test_an_example_that_cannot_be_made_to_fit_is_dropped(generator): + """Silence beats a truncated JSON document: half an object is not an + example of anything, and it is not what the route returned.""" + # A wide object, not a long string: strings and lists are already capped + # inside redact, so an object with hundreds of keys is the shape that can + # still come out over the limit. + huge = {'status': 'success', + 'data': {'field%03d' % index: 'value' for index in range(500)}} + assert generator._best_example([huge]) is None + # And a smaller sample of the same operation still wins, rather than the + # whole operation losing its example to one oversized transcript. + small = {'status': 'success', 'data': {'assetid': 1}} + assert generator._best_example([huge, small]) == small + + +def test_a_delete_that_reads_a_body_says_so(generator): + """DELETE /api/applications/{id} refuses without {"confirm": "OK"}. + + The generator declared a body for POST, PUT and PATCH only, so that route + published a call that cannot succeed and a client built from the spec fails + on the day. + + Evidence is the HANDLER reading a body (`readsbody`, derived from its AST), + not a harvested transcript. A transcript proves the test CLIENT sent + something, and the suite sends {} to routes that ignore it: on 2026-09-18 + transcript-only evidence published "this route reads a JSON body" on DELETE + /api/endpoints/agentkey, whose handler never calls get_json. Both halves are + pinned below. + """ + entries = [ + {'method': 'DELETE', 'path': '/api/applications/', + 'auth': 'permission:applications.delete', 'purpose': 'Delete it.', + 'readsbody': True, 'requestexample': {'confirm': 'OK'}}, + {'method': 'DELETE', 'path': '/api/assets/', + 'auth': 'permission:assets.delete', 'purpose': 'Delete it.'}, + # A transcript with no handler evidence: the suite sent a body, the + # route ignores it, so the contract must stay silent. + {'method': 'DELETE', 'path': '/api/endpoints/agentkey', + 'auth': 'permission:endpoints.manage', 'purpose': 'Revoke it.', + 'requestexample': {}}, + ] + built = generator.build([{'surface': 'core-applications', 'endpoints': entries}]) + withbody = built['paths']['/api/applications/{app_id}']['delete'] + media = withbody['requestBody']['content']['application/json'] + assert withbody['requestBody']['required'] is False + assert media['example'] == {'confirm': 'OK'} + # Transcript without handler evidence declares nothing. + ignored = built['paths']['/api/endpoints/agentkey']['delete'] + assert 'requestBody' not in ignored + assert 'requestBody' not in built['paths']['/api/assets/{assetid}']['delete'] diff --git a/tests/test_openapi_spec.py b/tests/test_openapi_spec.py index 45eb1570..8de1aa8a 100644 --- a/tests/test_openapi_spec.py +++ b/tests/test_openapi_spec.py @@ -189,3 +189,58 @@ def test_summaries_are_not_cut_mid_word(spec): if body and not body[-1].isalnum() and body[-1] not in ')]"\'': offenders.append('%s %s: %r' % (verb.upper(), path, summary[-40:])) assert not offenders, '\n '.join(offenders[:15]) + + +def test_every_parameter_is_well_formed(spec): + """A parameter with no name, no location or no schema is not a parameter. + + Query parameters are derived from each handler's AST now rather than left + out, which is 87 operations that could not be filtered, searched or paged + from this document before. Derived means a code change can produce a + malformed one, so the shape is checked here rather than assumed. + """ + offenders = [] + for path, verb, operation in operations(spec): + for parameter in operation.get('parameters', []): + where = '%s %s: %r' % (verb.upper(), path, parameter.get('name')) + if not parameter.get('name'): + offenders.append('%s %s: a parameter with no name' % (verb.upper(), path)) + if parameter.get('in') not in ('path', 'query', 'header', 'cookie'): + offenders.append(where + ' has no valid `in`') + if not isinstance(parameter.get('required'), bool): + offenders.append(where + ' does not say whether it is required') + if not parameter.get('schema'): + offenders.append(where + ' has no schema') + if parameter.get('in') == 'path' and not parameter.get('required'): + offenders.append(where + ' is in the path and optional, which ' + 'OpenAPI does not allow') + assert not offenders, '\n '.join(offenders[:15]) + + +def test_no_parameter_is_declared_twice(spec): + """(name, in) is the identity of a parameter. Two entries for one name is + ambiguous to a generated client, which picks one and drops the other.""" + offenders = [] + for path, verb, operation in operations(spec): + seen = set() + for parameter in operation.get('parameters', []): + key = (parameter.get('name'), parameter.get('in')) + if key in seen: + offenders.append('%s %s: %s in %s' + % (verb.upper(), path, key[0], key[1])) + seen.add(key) + assert not offenders, '\n '.join(offenders[:15]) + + +def test_a_query_parameter_is_not_the_path_argument_under_another_name(spec): + """`/api/assets/{assetid}?assetid=` is a contradiction: the same value in + two places, and a generated client has to guess which one the route reads.""" + offenders = [] + for path, verb, operation in operations(spec): + intemplate = set(re.findall(r'\{([^}]+)\}', path)) + query = {p['name'] for p in operation.get('parameters', []) + if p.get('in') == 'query'} + clash = sorted(intemplate & query) + if clash: + offenders.append('%s %s: %s' % (verb.upper(), path, clash)) + assert not offenders, '\n '.join(offenders[:15]) From 68567d9a279d8b7992efd564f744deb853326da4 Mon Sep 17 00:00:00 2001 From: cproudlock Date: Fri, 18 Sep 2026 20:18:23 -0400 Subject: [PATCH 2/6] Add a try-it API page, and stop the docs page reaching for the internet /api/docs renders the spec with Redoc, which is read-only by design. The thing people mean when they ask for Swagger is firing a request from the page, so Swagger UI now sits beside it at /api/docs/try, reading the same spec - one contract, two renderers. It is OFF outside development and 404s rather than rendering a disabled page: a production box holds live fleet data, and a docs page that can POST to it is a different object from a docs page that cannot. Vendored, not linked. swagger-ui-bundle.min.js and swagger-ui.css are served from staticdocs/ beside the existing Redoc bundle, because production has no internet. validatorUrl is null: left unset, Swagger UI hands the spec to the public swagger validator to render a badge, which here is a request that cannot succeed and elsewhere would ship the internal API surface off the box. THE VENDORED REDOC BUNDLE WAS ALREADY REACHING THE INTERNET, found while proving this page does not. redoc.standalone.js fetched cdn.redoc.ly/redoc/logo-mini.svg for the "API docs by Redocly" mark, so every load of /api/docs on the air-gapped server has been waiting on a request that cannot complete. Replaced with an inline transparent GIF. Three gate holes closed, each one a dodge that worked: - the page is built by interpolating url_for output into a JS string literal, unescaped. SCRIPT_NAME is proxy-controlled and ProxyFix honours X-Forwarded-Prefix, so a mount path carrying a quote closed the literal and executed what followed. The spec URL is now json.dumps'd and the rest HTML-escaped; the injected quote renders as \" and is inert - the "no internet" assertion on the page scanned for https?://, so ' - '' % (spec_url, redoc_url) + '' % (tryit_note, spec_url, redoc_url) ) return Response(page, mimetype='text/html') @@ -59,3 +119,92 @@ def llms_txt(): if not os.path.isfile(_LLMS): return Response('llms.txt not found', status=404, mimetype='text/plain') return send_file(_LLMS, mimetype='text/plain') + + +def swagger_tryit(): + """Swagger UI over the SAME spec the Redoc page renders. + + Every URL on the page comes from url_for, so the spec and both assets stay + same-origin and correct under a mount path (prod runs under /shopdb). + """ + spec_url = url_for('docs.openapi_spec') + css_url = url_for('docs.swagger_css') + js_url = url_for('docs.swagger_js') + redoc_page_url = url_for('docs.docs_index') + page = ( + '' + 'ShopDB API - try it' + '' + '' + '' + '' + '' + '

Execute sends a real request to this ' + 'server, as whoever is signed in on this browser. A write ' + 'operation changes live data. The read-only contract is at ' + '/api/docs.

' + '
' + '' + '' + '' + % {'css': escape(css_url), 'js': escape(js_url), + 'spec': json.dumps(spec_url), + 'redoc': escape(redoc_page_url)} + ) + return Response(page, mimetype='text/html') + + +def swagger_css(): + """Vendored Swagger UI stylesheet (offline).""" + return send_file(_SWAGGER_CSS, mimetype='text/css') + + +def swagger_js(): + """Vendored Swagger UI bundle (offline).""" + return send_file(_SWAGGER_JS, mimetype='application/javascript') + + +@docs_bp.record_once +def _mount_tryit(state): + """Bind the try-it routes only when the flag is on for this app. + + Binding at registration rather than checking the flag inside the view, so + that when it is off the routes DO NOT EXIST: the URL gets Flask's own 404 + and confirms nothing, and the unauthenticated-GET sweep in + tests/test_core/test_public_read_surface.py never has to make an exception + for three routes that are only meant to be reachable on a dev box. + """ + if not state.app.config.get('API_DOCS_TRYIT_ENABLED'): + return + state.add_url_rule('/try', endpoint='swagger_tryit', + view_func=swagger_tryit, strict_slashes=False) + state.add_url_rule('/swagger-ui.css', endpoint='swagger_css', + view_func=swagger_css) + state.add_url_rule('/swagger-ui-bundle.min.js', endpoint='swagger_js', + view_func=swagger_js) diff --git a/shopdb/core/api/staticdocs/redoc.standalone.js b/shopdb/core/api/staticdocs/redoc.standalone.js index 1f14e14a..db6101ec 100644 --- a/shopdb/core/api/staticdocs/redoc.standalone.js +++ b/shopdb/core/api/staticdocs/redoc.standalone.js @@ -1683,7 +1683,7 @@ padding: ${e=>4*e.theme.spacing.unit}px; `;Object.defineProperty,Object.getOwnPropertyDescriptor;let Ax=class extends r.Component{render(){const{operation:e}=this.props,t=e.codeSamples,n=t.length>0,i=1===t.length&&this.context.hideSingleRequestSampleTab;return n&&r.createElement("div",null,r.createElement(bp,null," ",hi("requestSamples")," "),r.createElement(xd,{defaultIndex:0},r.createElement(pd,{hidden:i},t.map(e=>r.createElement(md,{key:e.lang+"_"+(e.label||"")},void 0!==e.label?e.label:e.lang))),t.map(e=>r.createElement(vd,{key:e.lang+"_"+(e.label||"")},op(e)?r.createElement("div",null,r.createElement(yx,{content:e.requestBodyContent})):r.createElement(tg,{lang:e.lang,source:e.source})))))||null}};Ax.contextType=Os,Ax=((e,t)=>{for(var r,n=t,i=e.length-1;i>=0;i--)(r=e[i])&&(n=r(n)||n);return n})([Fg],Ax);Object.defineProperty,Object.getOwnPropertyDescriptor;let Cx=class extends r.Component{render(){const{operation:e}=this.props,t=e.responses.filter(e=>e.content&&e.content.hasSample);return t.length>0&&r.createElement("div",null,r.createElement(bp,null," ",hi("responseSamples")," "),r.createElement(xd,{defaultIndex:0},r.createElement(pd,null,t.map(e=>r.createElement(md,{className:"tab-"+e.type,key:e.code},e.code))),t.map(e=>r.createElement(vd,{key:e.code},r.createElement("div",null,r.createElement(yx,{content:e.content}))))))||null}};Cx=((e,t)=>{for(var r,n=t,i=e.length-1;i>=0;i--)(r=e[i])&&(n=r(n)||n);return n})([Fg],Cx);var jx=Object.defineProperty,Tx=Object.defineProperties,Nx=Object.getOwnPropertyDescriptors,Ix=Object.getOwnPropertySymbols,Rx=Object.prototype.hasOwnProperty,Lx=Object.prototype.propertyIsEnumerable,Dx=(e,t,r)=>t in e?jx(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;const Mx=gs.div` margin-bottom: ${({theme:e})=>6*e.spacing.unit}px; -`,zx=Fg(({operation:e})=>{const{name:t,description:n,deprecated:i,externalDocs:o,isWebhook:s,httpVerb:a,badges:l}=e,c=!(!n&&!o),{showWebhookVerb:u}=r.useContext(Os),p=l.filter(({position:e})=>"before"===e),d=l.filter(({position:e})=>"after"===e);return r.createElement(Os.Consumer,null,l=>r.createElement(dp,((e,t)=>Tx(e,Nx(t)))(((e,t)=>{for(var r in t||(t={}))Rx.call(t,r)&&Dx(e,r,t[r]);if(Ix)for(var r of Ix(t))Lx.call(t,r)&&Dx(e,r,t[r]);return e})({},{[eh]:e.operationHash}),{id:e.operationHash}),r.createElement(lp,null,r.createElement(gp,null,r.createElement(_p,{to:e.id}),p.map(({name:e,color:t})=>r.createElement(Ap,{type:"primary",key:e,color:t},e)),t," ",i&&r.createElement(Ap,{type:"warning"}," Deprecated "),s&&r.createElement(Ap,{type:"primary"}," ","Webhook ",u&&a&&"| "+a.toUpperCase()),d.map(({name:e,color:t})=>r.createElement(Ap,{type:"primary",key:e,color:t},e))),l.pathInMiddlePanel&&!s&&r.createElement(Sv,{operation:e,inverted:!0}),c&&r.createElement(Mx,null,void 0!==n&&r.createElement(Im,{source:n}),o&&r.createElement(ry,{externalDocs:o})),r.createElement(ay,{extensions:e.extensions}),r.createElement(tx,{securities:e.security}),r.createElement(Nv,{parameters:e.parameters,body:e.requestBody}),r.createElement(Jv,{responses:e.responses}),r.createElement(ax,{callbacks:e.callbacks})),r.createElement(pp,null,!l.pathInMiddlePanel&&!s&&r.createElement(Sv,{operation:e}),r.createElement(Ax,{operation:e}),r.createElement(Cx,{operation:e}),r.createElement(Px,{callbacks:e.callbacks}))))});var Fx=Object.defineProperty,Bx=Object.getOwnPropertyDescriptor,Ux=Object.getOwnPropertySymbols,qx=Object.prototype.hasOwnProperty,Vx=Object.prototype.propertyIsEnumerable,Wx=(e,t,r)=>t in e?Fx(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,Hx=(e,t,r,n)=>{for(var i,o=n>1?void 0:n?Bx(t,r):t,s=e.length-1;s>=0;s--)(i=e[s])&&(o=(n?i(t,r,o):i(o))||o);return n&&o&&Fx(t,r,o),o};let Gx=class extends r.Component{render(){const e=this.props.items;return 0===e.length?null:e.map(e=>r.createElement(Yx,{key:e.id,item:e}))}};Gx=Hx([Fg],Gx);let Yx=class extends r.Component{render(){const e=this.props.item;let t;const{type:n}=e;switch(n){case"group":t=null;break;case"tag":case"section":default:t=r.createElement(Qx,((e,t)=>{for(var r in t||(t={}))qx.call(t,r)&&Wx(e,r,t[r]);if(Ux)for(var r of Ux(t))Vx.call(t,r)&&Wx(e,r,t[r]);return e})({},this.props));break;case"operation":t=r.createElement(Xx,{item:e})}return r.createElement(r.Fragment,null,t&&r.createElement(cp,{id:e.id,$underlined:"operation"===e.type},t),e.items&&r.createElement(Gx,{items:e.items}))}};Yx=Hx([Fg],Yx);const Kx=e=>r.createElement(lp,{$compact:!0},e);let Qx=class extends r.Component{render(){const{name:e,description:t,externalDocs:n,level:i}=this.props.item,o=2===i?yp:gp;return r.createElement(r.Fragment,null,r.createElement(dp,null,r.createElement(lp,{$compact:!1},r.createElement(o,null,r.createElement(_p,{to:this.props.item.id}),e))),r.createElement(ev,{parentId:this.props.item.id,source:t||"",htmlWrap:Kx}),n&&r.createElement(dp,null,r.createElement(lp,null,r.createElement(ry,{externalDocs:n}))))}};Qx=Hx([Fg],Qx);let Xx=class extends r.Component{render(){return r.createElement(zx,{operation:this.props.item})}};Xx=Hx([Fg],Xx);var Jx=Object.defineProperty,Zx=Object.defineProperties,ew=(Object.getOwnPropertyDescriptor,Object.getOwnPropertyDescriptors),tw=Object.getOwnPropertySymbols,rw=Object.prototype.hasOwnProperty,nw=Object.prototype.propertyIsEnumerable,iw=(e,t,r)=>t in e?Jx(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;let ow=class extends r.Component{constructor(){super(...arguments),this.ref=r.createRef(),this.activate=e=>{this.props.onActivate(this.props.item),e.stopPropagation()}}componentDidMount(){this.scrollIntoViewIfActive()}componentDidUpdate(){this.scrollIntoViewIfActive()}scrollIntoViewIfActive(){this.props.item.active&&this.ref.current&&Jn(this.ref.current)}render(){const{item:e,withoutChildren:t}=this.props;return r.createElement(ov,{tabIndex:0,onClick:this.activate,onKeyDown:e=>{"Enter"!==e.key&&" "!==e.key||(this.props.onActivate(this.props.item),e.stopPropagation())},depth:e.depth,"data-item-id":e.id,role:"menuitem","aria-label":e.sidebarLabel,"aria-expanded":e.expanded},"operation"===e.type?r.createElement(sw,((e,t)=>Zx(e,ew(t)))(((e,t)=>{for(var r in t||(t={}))rw.call(t,r)&&iw(e,r,t[r]);if(tw)for(var r of tw(t))nw.call(t,r)&&iw(e,r,t[r]);return e})({},this.props),{item:e})):r.createElement(av,{$depth:e.depth,$active:e.active,$type:e.type,ref:this.ref},"schema"===e.type&&r.createElement(rv,{type:"schema"},"schema"),r.createElement(lv,{width:"calc(100% - 38px)",title:e.sidebarLabel},e.sidebarLabel,this.props.children),e.depth>0&&e.items.length>0&&r.createElement($p,{float:"right",direction:e.expanded?"down":"right"})||null),!t&&e.items&&e.items.length>0&&r.createElement(dw,{expanded:e.expanded,items:e.items,onActivate:this.props.onActivate}))}};ow=((e,t)=>{for(var r,n=t,i=e.length-1;i>=0;i--)(r=e[i])&&(n=r(n)||n);return n})([Fg],ow);const sw=Fg(e=>{var t;const{item:n}=e,i=r.createRef(),{showWebhookVerb:o}=r.useContext(Os);return r.useEffect(()=>{e.item.active&&i.current&&Jn(i.current)},[e.item.active,i]),r.createElement(av,{$depth:n.depth,$active:n.active,$deprecated:n.deprecated,ref:i},n.badges&&(null==(t=n.badges)?void 0:t.map(({name:e,color:t})=>r.createElement(rv,{type:"badge",color:t,key:e},e))),n.isWebhook?r.createElement(rv,{type:"hook"},o?n.httpVerb:hi("webhook")):r.createElement(rv,{type:n.httpVerb},ba(n.httpVerb)),r.createElement(lv,{tabIndex:0,width:"calc(100% - 38px)"},n.sidebarLabel,e.children))});var aw=Object.defineProperty,lw=(Object.getOwnPropertyDescriptor,Object.getOwnPropertySymbols),cw=Object.prototype.hasOwnProperty,uw=Object.prototype.propertyIsEnumerable,pw=(e,t,r)=>t in e?aw(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;let dw=class extends r.Component{render(){const{items:e,root:t,className:n}=this.props,i=null==this.props.expanded||this.props.expanded;return r.createElement(iv,((e,t)=>{for(var r in t||(t={}))cw.call(t,r)&&pw(e,r,t[r]);if(lw)for(var r of lw(t))uw.call(t,r)&&pw(e,r,t[r]);return e})({className:n,style:this.props.style,$expanded:i},t?{role:"menu"}:{}),e.map((e,t)=>r.createElement(ow,{key:t,item:e,onActivate:this.props.onActivate})))}};function fw(){const[e,t]=(0,r.useState)(!1);return(0,r.useEffect)(()=>{t(!0)},[]),e?r.createElement("img",{alt:"redocly logo",onError:()=>t(!1),src:"https://cdn.redoc.ly/redoc/logo-mini.svg"}):null}dw=((e,t)=>{for(var r,n=t,i=e.length-1;i>=0;i--)(r=e[i])&&(n=r(n)||n);return n})([Fg],dw);Object.defineProperty,Object.getOwnPropertyDescriptor;let hw=class extends r.Component{constructor(){super(...arguments),this.activate=e=>{if(e&&e.active&&this.context.menuToggle)return e.expanded?e.collapse():e.expand();this.props.menu.activateAndScroll(e,!0),setTimeout(()=>{this._updateScroll&&this._updateScroll()})},this.saveScrollUpdate=e=>{this._updateScroll=e}}render(){const e=this.props.menu;return r.createElement(df,{updateFn:this.saveScrollUpdate,className:this.props.className,options:{wheelPropagation:!1}},r.createElement(dw,{items:e.items,onActivate:this.activate,root:!0}),r.createElement(cv,null,r.createElement("a",{target:"_blank",rel:"noopener noreferrer",href:"https://redocly.com/redoc/"},r.createElement(fw,null),"API docs by Redocly")))}};hw.contextType=Os,hw=((e,t)=>{for(var r,n=t,i=e.length-1;i>=0;i--)(r=e[i])&&(n=r(n)||n);return n})([Fg],hw);const mw=({open:e})=>{const t=e?8:-4;return r.createElement(yw,null,r.createElement(gw,{size:15,style:{transform:`translate(2px, ${t}px) rotate(180deg)`,transition:"transform 0.2s ease"}}),r.createElement(gw,{size:15,style:{transform:`translate(2px, ${0-t}px)`,transition:"transform 0.2s ease"}}))},gw=({size:e=10,className:t="",style:n})=>r.createElement("svg",{className:t,style:n||{},viewBox:"0 0 926.23699 573.74994",version:"1.1",x:"0px",y:"0px",width:e,height:e},r.createElement("g",{transform:"translate(904.92214,-879.1482)"},r.createElement("path",{d:"\n m -673.67664,1221.6502 -231.2455,-231.24803 55.6165,\n -55.627 c 30.5891,-30.59485 56.1806,-55.627 56.8701,-55.627 0.6894,\n 0 79.8637,78.60862 175.9427,174.68583 l 174.6892,174.6858 174.6892,\n -174.6858 c 96.079,-96.07721 175.253196,-174.68583 175.942696,\n -174.68583 0.6895,0 26.281,25.03215 56.8701,\n 55.627 l 55.6165,55.627 -231.245496,231.24803 c -127.185,127.1864\n -231.5279,231.248 -231.873,231.248 -0.3451,0 -104.688,\n -104.0616 -231.873,-231.248 z\n ",fill:"currentColor"}))),yw=gs.div` +`,zx=Fg(({operation:e})=>{const{name:t,description:n,deprecated:i,externalDocs:o,isWebhook:s,httpVerb:a,badges:l}=e,c=!(!n&&!o),{showWebhookVerb:u}=r.useContext(Os),p=l.filter(({position:e})=>"before"===e),d=l.filter(({position:e})=>"after"===e);return r.createElement(Os.Consumer,null,l=>r.createElement(dp,((e,t)=>Tx(e,Nx(t)))(((e,t)=>{for(var r in t||(t={}))Rx.call(t,r)&&Dx(e,r,t[r]);if(Ix)for(var r of Ix(t))Lx.call(t,r)&&Dx(e,r,t[r]);return e})({},{[eh]:e.operationHash}),{id:e.operationHash}),r.createElement(lp,null,r.createElement(gp,null,r.createElement(_p,{to:e.id}),p.map(({name:e,color:t})=>r.createElement(Ap,{type:"primary",key:e,color:t},e)),t," ",i&&r.createElement(Ap,{type:"warning"}," Deprecated "),s&&r.createElement(Ap,{type:"primary"}," ","Webhook ",u&&a&&"| "+a.toUpperCase()),d.map(({name:e,color:t})=>r.createElement(Ap,{type:"primary",key:e,color:t},e))),l.pathInMiddlePanel&&!s&&r.createElement(Sv,{operation:e,inverted:!0}),c&&r.createElement(Mx,null,void 0!==n&&r.createElement(Im,{source:n}),o&&r.createElement(ry,{externalDocs:o})),r.createElement(ay,{extensions:e.extensions}),r.createElement(tx,{securities:e.security}),r.createElement(Nv,{parameters:e.parameters,body:e.requestBody}),r.createElement(Jv,{responses:e.responses}),r.createElement(ax,{callbacks:e.callbacks})),r.createElement(pp,null,!l.pathInMiddlePanel&&!s&&r.createElement(Sv,{operation:e}),r.createElement(Ax,{operation:e}),r.createElement(Cx,{operation:e}),r.createElement(Px,{callbacks:e.callbacks}))))});var Fx=Object.defineProperty,Bx=Object.getOwnPropertyDescriptor,Ux=Object.getOwnPropertySymbols,qx=Object.prototype.hasOwnProperty,Vx=Object.prototype.propertyIsEnumerable,Wx=(e,t,r)=>t in e?Fx(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,Hx=(e,t,r,n)=>{for(var i,o=n>1?void 0:n?Bx(t,r):t,s=e.length-1;s>=0;s--)(i=e[s])&&(o=(n?i(t,r,o):i(o))||o);return n&&o&&Fx(t,r,o),o};let Gx=class extends r.Component{render(){const e=this.props.items;return 0===e.length?null:e.map(e=>r.createElement(Yx,{key:e.id,item:e}))}};Gx=Hx([Fg],Gx);let Yx=class extends r.Component{render(){const e=this.props.item;let t;const{type:n}=e;switch(n){case"group":t=null;break;case"tag":case"section":default:t=r.createElement(Qx,((e,t)=>{for(var r in t||(t={}))qx.call(t,r)&&Wx(e,r,t[r]);if(Ux)for(var r of Ux(t))Vx.call(t,r)&&Wx(e,r,t[r]);return e})({},this.props));break;case"operation":t=r.createElement(Xx,{item:e})}return r.createElement(r.Fragment,null,t&&r.createElement(cp,{id:e.id,$underlined:"operation"===e.type},t),e.items&&r.createElement(Gx,{items:e.items}))}};Yx=Hx([Fg],Yx);const Kx=e=>r.createElement(lp,{$compact:!0},e);let Qx=class extends r.Component{render(){const{name:e,description:t,externalDocs:n,level:i}=this.props.item,o=2===i?yp:gp;return r.createElement(r.Fragment,null,r.createElement(dp,null,r.createElement(lp,{$compact:!1},r.createElement(o,null,r.createElement(_p,{to:this.props.item.id}),e))),r.createElement(ev,{parentId:this.props.item.id,source:t||"",htmlWrap:Kx}),n&&r.createElement(dp,null,r.createElement(lp,null,r.createElement(ry,{externalDocs:n}))))}};Qx=Hx([Fg],Qx);let Xx=class extends r.Component{render(){return r.createElement(zx,{operation:this.props.item})}};Xx=Hx([Fg],Xx);var Jx=Object.defineProperty,Zx=Object.defineProperties,ew=(Object.getOwnPropertyDescriptor,Object.getOwnPropertyDescriptors),tw=Object.getOwnPropertySymbols,rw=Object.prototype.hasOwnProperty,nw=Object.prototype.propertyIsEnumerable,iw=(e,t,r)=>t in e?Jx(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;let ow=class extends r.Component{constructor(){super(...arguments),this.ref=r.createRef(),this.activate=e=>{this.props.onActivate(this.props.item),e.stopPropagation()}}componentDidMount(){this.scrollIntoViewIfActive()}componentDidUpdate(){this.scrollIntoViewIfActive()}scrollIntoViewIfActive(){this.props.item.active&&this.ref.current&&Jn(this.ref.current)}render(){const{item:e,withoutChildren:t}=this.props;return r.createElement(ov,{tabIndex:0,onClick:this.activate,onKeyDown:e=>{"Enter"!==e.key&&" "!==e.key||(this.props.onActivate(this.props.item),e.stopPropagation())},depth:e.depth,"data-item-id":e.id,role:"menuitem","aria-label":e.sidebarLabel,"aria-expanded":e.expanded},"operation"===e.type?r.createElement(sw,((e,t)=>Zx(e,ew(t)))(((e,t)=>{for(var r in t||(t={}))rw.call(t,r)&&iw(e,r,t[r]);if(tw)for(var r of tw(t))nw.call(t,r)&&iw(e,r,t[r]);return e})({},this.props),{item:e})):r.createElement(av,{$depth:e.depth,$active:e.active,$type:e.type,ref:this.ref},"schema"===e.type&&r.createElement(rv,{type:"schema"},"schema"),r.createElement(lv,{width:"calc(100% - 38px)",title:e.sidebarLabel},e.sidebarLabel,this.props.children),e.depth>0&&e.items.length>0&&r.createElement($p,{float:"right",direction:e.expanded?"down":"right"})||null),!t&&e.items&&e.items.length>0&&r.createElement(dw,{expanded:e.expanded,items:e.items,onActivate:this.props.onActivate}))}};ow=((e,t)=>{for(var r,n=t,i=e.length-1;i>=0;i--)(r=e[i])&&(n=r(n)||n);return n})([Fg],ow);const sw=Fg(e=>{var t;const{item:n}=e,i=r.createRef(),{showWebhookVerb:o}=r.useContext(Os);return r.useEffect(()=>{e.item.active&&i.current&&Jn(i.current)},[e.item.active,i]),r.createElement(av,{$depth:n.depth,$active:n.active,$deprecated:n.deprecated,ref:i},n.badges&&(null==(t=n.badges)?void 0:t.map(({name:e,color:t})=>r.createElement(rv,{type:"badge",color:t,key:e},e))),n.isWebhook?r.createElement(rv,{type:"hook"},o?n.httpVerb:hi("webhook")):r.createElement(rv,{type:n.httpVerb},ba(n.httpVerb)),r.createElement(lv,{tabIndex:0,width:"calc(100% - 38px)"},n.sidebarLabel,e.children))});var aw=Object.defineProperty,lw=(Object.getOwnPropertyDescriptor,Object.getOwnPropertySymbols),cw=Object.prototype.hasOwnProperty,uw=Object.prototype.propertyIsEnumerable,pw=(e,t,r)=>t in e?aw(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;let dw=class extends r.Component{render(){const{items:e,root:t,className:n}=this.props,i=null==this.props.expanded||this.props.expanded;return r.createElement(iv,((e,t)=>{for(var r in t||(t={}))cw.call(t,r)&&pw(e,r,t[r]);if(lw)for(var r of lw(t))uw.call(t,r)&&pw(e,r,t[r]);return e})({className:n,style:this.props.style,$expanded:i},t?{role:"menu"}:{}),e.map((e,t)=>r.createElement(ow,{key:t,item:e,onActivate:this.props.onActivate})))}};function fw(){const[e,t]=(0,r.useState)(!1);return(0,r.useEffect)(()=>{t(!0)},[]),e?r.createElement("img",{alt:"redocly logo",onError:()=>t(!1),src:"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="}):null}dw=((e,t)=>{for(var r,n=t,i=e.length-1;i>=0;i--)(r=e[i])&&(n=r(n)||n);return n})([Fg],dw);Object.defineProperty,Object.getOwnPropertyDescriptor;let hw=class extends r.Component{constructor(){super(...arguments),this.activate=e=>{if(e&&e.active&&this.context.menuToggle)return e.expanded?e.collapse():e.expand();this.props.menu.activateAndScroll(e,!0),setTimeout(()=>{this._updateScroll&&this._updateScroll()})},this.saveScrollUpdate=e=>{this._updateScroll=e}}render(){const e=this.props.menu;return r.createElement(df,{updateFn:this.saveScrollUpdate,className:this.props.className,options:{wheelPropagation:!1}},r.createElement(dw,{items:e.items,onActivate:this.activate,root:!0}),r.createElement(cv,null,r.createElement("a",{target:"_blank",rel:"noopener noreferrer",href:"https://redocly.com/redoc/"},r.createElement(fw,null),"API docs by Redocly")))}};hw.contextType=Os,hw=((e,t)=>{for(var r,n=t,i=e.length-1;i>=0;i--)(r=e[i])&&(n=r(n)||n);return n})([Fg],hw);const mw=({open:e})=>{const t=e?8:-4;return r.createElement(yw,null,r.createElement(gw,{size:15,style:{transform:`translate(2px, ${t}px) rotate(180deg)`,transition:"transform 0.2s ease"}}),r.createElement(gw,{size:15,style:{transform:`translate(2px, ${0-t}px)`,transition:"transform 0.2s ease"}}))},gw=({size:e=10,className:t="",style:n})=>r.createElement("svg",{className:t,style:n||{},viewBox:"0 0 926.23699 573.74994",version:"1.1",x:"0px",y:"0px",width:e,height:e},r.createElement("g",{transform:"translate(904.92214,-879.1482)"},r.createElement("path",{d:"\n m -673.67664,1221.6502 -231.2455,-231.24803 55.6165,\n -55.627 c 30.5891,-30.59485 56.1806,-55.627 56.8701,-55.627 0.6894,\n 0 79.8637,78.60862 175.9427,174.68583 l 174.6892,174.6858 174.6892,\n -174.6858 c 96.079,-96.07721 175.253196,-174.68583 175.942696,\n -174.68583 0.6895,0 26.281,25.03215 56.8701,\n 55.627 l 55.6165,55.627 -231.245496,231.24803 c -127.185,127.1864\n -231.5279,231.248 -231.873,231.248 -0.3451,0 -104.688,\n -104.0616 -231.873,-231.248 z\n ",fill:"currentColor"}))),yw=gs.div` user-select: none; width: 20px; height: 20px; diff --git a/shopdb/core/api/staticdocs/swagger-ui-bundle.min.js b/shopdb/core/api/staticdocs/swagger-ui-bundle.min.js new file mode 100644 index 00000000..c02a7887 --- /dev/null +++ b/shopdb/core/api/staticdocs/swagger-ui-bundle.min.js @@ -0,0 +1,2 @@ +/*! For license information please see swagger-ui-bundle.js.LICENSE.txt */ +!function webpackUniversalModuleDefinition(s,o){"object"==typeof exports&&"object"==typeof module?module.exports=o():"function"==typeof define&&define.amd?define([],o):"object"==typeof exports?exports.SwaggerUIBundle=o():s.SwaggerUIBundle=o()}(this,(()=>(()=>{var s={67526(s,o){"use strict";o.byteLength=function byteLength(s){var o=getLens(s),i=o[0],a=o[1];return 3*(i+a)/4-a},o.toByteArray=function toByteArray(s){var o,i,_=getLens(s),w=_[0],x=_[1],C=new u(function _byteLength(s,o,i){return 3*(o+i)/4-i}(0,w,x)),j=0,L=x>0?w-4:w;for(i=0;i>16&255,C[j++]=o>>8&255,C[j++]=255&o;2===x&&(o=a[s.charCodeAt(i)]<<2|a[s.charCodeAt(i+1)]>>4,C[j++]=255&o);1===x&&(o=a[s.charCodeAt(i)]<<10|a[s.charCodeAt(i+1)]<<4|a[s.charCodeAt(i+2)]>>2,C[j++]=o>>8&255,C[j++]=255&o);return C},o.fromByteArray=function fromByteArray(s){for(var o,a=s.length,u=a%3,_=[],w=16383,x=0,C=a-u;xC?C:x+w));1===u?(o=s[a-1],_.push(i[o>>2]+i[o<<4&63]+"==")):2===u&&(o=(s[a-2]<<8)+s[a-1],_.push(i[o>>10]+i[o>>4&63]+i[o<<2&63]+"="));return _.join("")};for(var i=[],a=[],u="undefined"!=typeof Uint8Array?Uint8Array:Array,_="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",w=0;w<64;++w)i[w]=_[w],a[_.charCodeAt(w)]=w;function getLens(s){var o=s.length;if(o%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var i=s.indexOf("=");return-1===i&&(i=o),[i,i===o?0:4-i%4]}function encodeChunk(s,o,a){for(var u,_,w=[],x=o;x>18&63]+i[_>>12&63]+i[_>>6&63]+i[63&_]);return w.join("")}a["-".charCodeAt(0)]=62,a["_".charCodeAt(0)]=63},48287(s,o,i){"use strict";const a=i(67526),u=i(251),_="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null;o.Buffer=Buffer,o.SlowBuffer=function SlowBuffer(s){+s!=s&&(s=0);return Buffer.alloc(+s)},o.INSPECT_MAX_BYTES=50;const w=2147483647;function createBuffer(s){if(s>w)throw new RangeError('The value "'+s+'" is invalid for option "size"');const o=new Uint8Array(s);return Object.setPrototypeOf(o,Buffer.prototype),o}function Buffer(s,o,i){if("number"==typeof s){if("string"==typeof o)throw new TypeError('The "string" argument must be of type string. Received type number');return allocUnsafe(s)}return from(s,o,i)}function from(s,o,i){if("string"==typeof s)return function fromString(s,o){"string"==typeof o&&""!==o||(o="utf8");if(!Buffer.isEncoding(o))throw new TypeError("Unknown encoding: "+o);const i=0|byteLength(s,o);let a=createBuffer(i);const u=a.write(s,o);u!==i&&(a=a.slice(0,u));return a}(s,o);if(ArrayBuffer.isView(s))return function fromArrayView(s){if(isInstance(s,Uint8Array)){const o=new Uint8Array(s);return fromArrayBuffer(o.buffer,o.byteOffset,o.byteLength)}return fromArrayLike(s)}(s);if(null==s)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof s);if(isInstance(s,ArrayBuffer)||s&&isInstance(s.buffer,ArrayBuffer))return fromArrayBuffer(s,o,i);if("undefined"!=typeof SharedArrayBuffer&&(isInstance(s,SharedArrayBuffer)||s&&isInstance(s.buffer,SharedArrayBuffer)))return fromArrayBuffer(s,o,i);if("number"==typeof s)throw new TypeError('The "value" argument must not be of type number. Received type number');const a=s.valueOf&&s.valueOf();if(null!=a&&a!==s)return Buffer.from(a,o,i);const u=function fromObject(s){if(Buffer.isBuffer(s)){const o=0|checked(s.length),i=createBuffer(o);return 0===i.length||s.copy(i,0,0,o),i}if(void 0!==s.length)return"number"!=typeof s.length||numberIsNaN(s.length)?createBuffer(0):fromArrayLike(s);if("Buffer"===s.type&&Array.isArray(s.data))return fromArrayLike(s.data)}(s);if(u)return u;if("undefined"!=typeof Symbol&&null!=Symbol.toPrimitive&&"function"==typeof s[Symbol.toPrimitive])return Buffer.from(s[Symbol.toPrimitive]("string"),o,i);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof s)}function assertSize(s){if("number"!=typeof s)throw new TypeError('"size" argument must be of type number');if(s<0)throw new RangeError('The value "'+s+'" is invalid for option "size"')}function allocUnsafe(s){return assertSize(s),createBuffer(s<0?0:0|checked(s))}function fromArrayLike(s){const o=s.length<0?0:0|checked(s.length),i=createBuffer(o);for(let a=0;a=w)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+w.toString(16)+" bytes");return 0|s}function byteLength(s,o){if(Buffer.isBuffer(s))return s.length;if(ArrayBuffer.isView(s)||isInstance(s,ArrayBuffer))return s.byteLength;if("string"!=typeof s)throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof s);const i=s.length,a=arguments.length>2&&!0===arguments[2];if(!a&&0===i)return 0;let u=!1;for(;;)switch(o){case"ascii":case"latin1":case"binary":return i;case"utf8":case"utf-8":return utf8ToBytes(s).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*i;case"hex":return i>>>1;case"base64":return base64ToBytes(s).length;default:if(u)return a?-1:utf8ToBytes(s).length;o=(""+o).toLowerCase(),u=!0}}function slowToString(s,o,i){let a=!1;if((void 0===o||o<0)&&(o=0),o>this.length)return"";if((void 0===i||i>this.length)&&(i=this.length),i<=0)return"";if((i>>>=0)<=(o>>>=0))return"";for(s||(s="utf8");;)switch(s){case"hex":return hexSlice(this,o,i);case"utf8":case"utf-8":return utf8Slice(this,o,i);case"ascii":return asciiSlice(this,o,i);case"latin1":case"binary":return latin1Slice(this,o,i);case"base64":return base64Slice(this,o,i);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return utf16leSlice(this,o,i);default:if(a)throw new TypeError("Unknown encoding: "+s);s=(s+"").toLowerCase(),a=!0}}function swap(s,o,i){const a=s[o];s[o]=s[i],s[i]=a}function bidirectionalIndexOf(s,o,i,a,u){if(0===s.length)return-1;if("string"==typeof i?(a=i,i=0):i>2147483647?i=2147483647:i<-2147483648&&(i=-2147483648),numberIsNaN(i=+i)&&(i=u?0:s.length-1),i<0&&(i=s.length+i),i>=s.length){if(u)return-1;i=s.length-1}else if(i<0){if(!u)return-1;i=0}if("string"==typeof o&&(o=Buffer.from(o,a)),Buffer.isBuffer(o))return 0===o.length?-1:arrayIndexOf(s,o,i,a,u);if("number"==typeof o)return o&=255,"function"==typeof Uint8Array.prototype.indexOf?u?Uint8Array.prototype.indexOf.call(s,o,i):Uint8Array.prototype.lastIndexOf.call(s,o,i):arrayIndexOf(s,[o],i,a,u);throw new TypeError("val must be string, number or Buffer")}function arrayIndexOf(s,o,i,a,u){let _,w=1,x=s.length,C=o.length;if(void 0!==a&&("ucs2"===(a=String(a).toLowerCase())||"ucs-2"===a||"utf16le"===a||"utf-16le"===a)){if(s.length<2||o.length<2)return-1;w=2,x/=2,C/=2,i/=2}function read(s,o){return 1===w?s[o]:s.readUInt16BE(o*w)}if(u){let a=-1;for(_=i;_x&&(i=x-C),_=i;_>=0;_--){let i=!0;for(let a=0;au&&(a=u):a=u;const _=o.length;let w;for(a>_/2&&(a=_/2),w=0;w>8,u=i%256,_.push(u),_.push(a);return _}(o,s.length-i),s,i,a)}function base64Slice(s,o,i){return 0===o&&i===s.length?a.fromByteArray(s):a.fromByteArray(s.slice(o,i))}function utf8Slice(s,o,i){i=Math.min(s.length,i);const a=[];let u=o;for(;u239?4:o>223?3:o>191?2:1;if(u+w<=i){let i,a,x,C;switch(w){case 1:o<128&&(_=o);break;case 2:i=s[u+1],128==(192&i)&&(C=(31&o)<<6|63&i,C>127&&(_=C));break;case 3:i=s[u+1],a=s[u+2],128==(192&i)&&128==(192&a)&&(C=(15&o)<<12|(63&i)<<6|63&a,C>2047&&(C<55296||C>57343)&&(_=C));break;case 4:i=s[u+1],a=s[u+2],x=s[u+3],128==(192&i)&&128==(192&a)&&128==(192&x)&&(C=(15&o)<<18|(63&i)<<12|(63&a)<<6|63&x,C>65535&&C<1114112&&(_=C))}}null===_?(_=65533,w=1):_>65535&&(_-=65536,a.push(_>>>10&1023|55296),_=56320|1023&_),a.push(_),u+=w}return function decodeCodePointsArray(s){const o=s.length;if(o<=x)return String.fromCharCode.apply(String,s);let i="",a=0;for(;aa.length?(Buffer.isBuffer(o)||(o=Buffer.from(o)),o.copy(a,u)):Uint8Array.prototype.set.call(a,o,u);else{if(!Buffer.isBuffer(o))throw new TypeError('"list" argument must be an Array of Buffers');o.copy(a,u)}u+=o.length}return a},Buffer.byteLength=byteLength,Buffer.prototype._isBuffer=!0,Buffer.prototype.swap16=function swap16(){const s=this.length;if(s%2!=0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(let o=0;oi&&(s+=" ... "),""},_&&(Buffer.prototype[_]=Buffer.prototype.inspect),Buffer.prototype.compare=function compare(s,o,i,a,u){if(isInstance(s,Uint8Array)&&(s=Buffer.from(s,s.offset,s.byteLength)),!Buffer.isBuffer(s))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof s);if(void 0===o&&(o=0),void 0===i&&(i=s?s.length:0),void 0===a&&(a=0),void 0===u&&(u=this.length),o<0||i>s.length||a<0||u>this.length)throw new RangeError("out of range index");if(a>=u&&o>=i)return 0;if(a>=u)return-1;if(o>=i)return 1;if(this===s)return 0;let _=(u>>>=0)-(a>>>=0),w=(i>>>=0)-(o>>>=0);const x=Math.min(_,w),C=this.slice(a,u),j=s.slice(o,i);for(let s=0;s>>=0,isFinite(i)?(i>>>=0,void 0===a&&(a="utf8")):(a=i,i=void 0)}const u=this.length-o;if((void 0===i||i>u)&&(i=u),s.length>0&&(i<0||o<0)||o>this.length)throw new RangeError("Attempt to write outside buffer bounds");a||(a="utf8");let _=!1;for(;;)switch(a){case"hex":return hexWrite(this,s,o,i);case"utf8":case"utf-8":return utf8Write(this,s,o,i);case"ascii":case"latin1":case"binary":return asciiWrite(this,s,o,i);case"base64":return base64Write(this,s,o,i);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return ucs2Write(this,s,o,i);default:if(_)throw new TypeError("Unknown encoding: "+a);a=(""+a).toLowerCase(),_=!0}},Buffer.prototype.toJSON=function toJSON(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};const x=4096;function asciiSlice(s,o,i){let a="";i=Math.min(s.length,i);for(let u=o;ua)&&(i=a);let u="";for(let a=o;ai)throw new RangeError("Trying to access beyond buffer length")}function checkInt(s,o,i,a,u,_){if(!Buffer.isBuffer(s))throw new TypeError('"buffer" argument must be a Buffer instance');if(o>u||o<_)throw new RangeError('"value" argument is out of bounds');if(i+a>s.length)throw new RangeError("Index out of range")}function wrtBigUInt64LE(s,o,i,a,u){checkIntBI(o,a,u,s,i,7);let _=Number(o&BigInt(4294967295));s[i++]=_,_>>=8,s[i++]=_,_>>=8,s[i++]=_,_>>=8,s[i++]=_;let w=Number(o>>BigInt(32)&BigInt(4294967295));return s[i++]=w,w>>=8,s[i++]=w,w>>=8,s[i++]=w,w>>=8,s[i++]=w,i}function wrtBigUInt64BE(s,o,i,a,u){checkIntBI(o,a,u,s,i,7);let _=Number(o&BigInt(4294967295));s[i+7]=_,_>>=8,s[i+6]=_,_>>=8,s[i+5]=_,_>>=8,s[i+4]=_;let w=Number(o>>BigInt(32)&BigInt(4294967295));return s[i+3]=w,w>>=8,s[i+2]=w,w>>=8,s[i+1]=w,w>>=8,s[i]=w,i+8}function checkIEEE754(s,o,i,a,u,_){if(i+a>s.length)throw new RangeError("Index out of range");if(i<0)throw new RangeError("Index out of range")}function writeFloat(s,o,i,a,_){return o=+o,i>>>=0,_||checkIEEE754(s,0,i,4),u.write(s,o,i,a,23,4),i+4}function writeDouble(s,o,i,a,_){return o=+o,i>>>=0,_||checkIEEE754(s,0,i,8),u.write(s,o,i,a,52,8),i+8}Buffer.prototype.slice=function slice(s,o){const i=this.length;(s=~~s)<0?(s+=i)<0&&(s=0):s>i&&(s=i),(o=void 0===o?i:~~o)<0?(o+=i)<0&&(o=0):o>i&&(o=i),o>>=0,o>>>=0,i||checkOffset(s,o,this.length);let a=this[s],u=1,_=0;for(;++_>>=0,o>>>=0,i||checkOffset(s,o,this.length);let a=this[s+--o],u=1;for(;o>0&&(u*=256);)a+=this[s+--o]*u;return a},Buffer.prototype.readUint8=Buffer.prototype.readUInt8=function readUInt8(s,o){return s>>>=0,o||checkOffset(s,1,this.length),this[s]},Buffer.prototype.readUint16LE=Buffer.prototype.readUInt16LE=function readUInt16LE(s,o){return s>>>=0,o||checkOffset(s,2,this.length),this[s]|this[s+1]<<8},Buffer.prototype.readUint16BE=Buffer.prototype.readUInt16BE=function readUInt16BE(s,o){return s>>>=0,o||checkOffset(s,2,this.length),this[s]<<8|this[s+1]},Buffer.prototype.readUint32LE=Buffer.prototype.readUInt32LE=function readUInt32LE(s,o){return s>>>=0,o||checkOffset(s,4,this.length),(this[s]|this[s+1]<<8|this[s+2]<<16)+16777216*this[s+3]},Buffer.prototype.readUint32BE=Buffer.prototype.readUInt32BE=function readUInt32BE(s,o){return s>>>=0,o||checkOffset(s,4,this.length),16777216*this[s]+(this[s+1]<<16|this[s+2]<<8|this[s+3])},Buffer.prototype.readBigUInt64LE=defineBigIntMethod((function readBigUInt64LE(s){validateNumber(s>>>=0,"offset");const o=this[s],i=this[s+7];void 0!==o&&void 0!==i||boundsError(s,this.length-8);const a=o+256*this[++s]+65536*this[++s]+this[++s]*2**24,u=this[++s]+256*this[++s]+65536*this[++s]+i*2**24;return BigInt(a)+(BigInt(u)<>>=0,"offset");const o=this[s],i=this[s+7];void 0!==o&&void 0!==i||boundsError(s,this.length-8);const a=o*2**24+65536*this[++s]+256*this[++s]+this[++s],u=this[++s]*2**24+65536*this[++s]+256*this[++s]+i;return(BigInt(a)<>>=0,o>>>=0,i||checkOffset(s,o,this.length);let a=this[s],u=1,_=0;for(;++_=u&&(a-=Math.pow(2,8*o)),a},Buffer.prototype.readIntBE=function readIntBE(s,o,i){s>>>=0,o>>>=0,i||checkOffset(s,o,this.length);let a=o,u=1,_=this[s+--a];for(;a>0&&(u*=256);)_+=this[s+--a]*u;return u*=128,_>=u&&(_-=Math.pow(2,8*o)),_},Buffer.prototype.readInt8=function readInt8(s,o){return s>>>=0,o||checkOffset(s,1,this.length),128&this[s]?-1*(255-this[s]+1):this[s]},Buffer.prototype.readInt16LE=function readInt16LE(s,o){s>>>=0,o||checkOffset(s,2,this.length);const i=this[s]|this[s+1]<<8;return 32768&i?4294901760|i:i},Buffer.prototype.readInt16BE=function readInt16BE(s,o){s>>>=0,o||checkOffset(s,2,this.length);const i=this[s+1]|this[s]<<8;return 32768&i?4294901760|i:i},Buffer.prototype.readInt32LE=function readInt32LE(s,o){return s>>>=0,o||checkOffset(s,4,this.length),this[s]|this[s+1]<<8|this[s+2]<<16|this[s+3]<<24},Buffer.prototype.readInt32BE=function readInt32BE(s,o){return s>>>=0,o||checkOffset(s,4,this.length),this[s]<<24|this[s+1]<<16|this[s+2]<<8|this[s+3]},Buffer.prototype.readBigInt64LE=defineBigIntMethod((function readBigInt64LE(s){validateNumber(s>>>=0,"offset");const o=this[s],i=this[s+7];void 0!==o&&void 0!==i||boundsError(s,this.length-8);const a=this[s+4]+256*this[s+5]+65536*this[s+6]+(i<<24);return(BigInt(a)<>>=0,"offset");const o=this[s],i=this[s+7];void 0!==o&&void 0!==i||boundsError(s,this.length-8);const a=(o<<24)+65536*this[++s]+256*this[++s]+this[++s];return(BigInt(a)<>>=0,o||checkOffset(s,4,this.length),u.read(this,s,!0,23,4)},Buffer.prototype.readFloatBE=function readFloatBE(s,o){return s>>>=0,o||checkOffset(s,4,this.length),u.read(this,s,!1,23,4)},Buffer.prototype.readDoubleLE=function readDoubleLE(s,o){return s>>>=0,o||checkOffset(s,8,this.length),u.read(this,s,!0,52,8)},Buffer.prototype.readDoubleBE=function readDoubleBE(s,o){return s>>>=0,o||checkOffset(s,8,this.length),u.read(this,s,!1,52,8)},Buffer.prototype.writeUintLE=Buffer.prototype.writeUIntLE=function writeUIntLE(s,o,i,a){if(s=+s,o>>>=0,i>>>=0,!a){checkInt(this,s,o,i,Math.pow(2,8*i)-1,0)}let u=1,_=0;for(this[o]=255&s;++_>>=0,i>>>=0,!a){checkInt(this,s,o,i,Math.pow(2,8*i)-1,0)}let u=i-1,_=1;for(this[o+u]=255&s;--u>=0&&(_*=256);)this[o+u]=s/_&255;return o+i},Buffer.prototype.writeUint8=Buffer.prototype.writeUInt8=function writeUInt8(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,1,255,0),this[o]=255&s,o+1},Buffer.prototype.writeUint16LE=Buffer.prototype.writeUInt16LE=function writeUInt16LE(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,2,65535,0),this[o]=255&s,this[o+1]=s>>>8,o+2},Buffer.prototype.writeUint16BE=Buffer.prototype.writeUInt16BE=function writeUInt16BE(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,2,65535,0),this[o]=s>>>8,this[o+1]=255&s,o+2},Buffer.prototype.writeUint32LE=Buffer.prototype.writeUInt32LE=function writeUInt32LE(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,4,4294967295,0),this[o+3]=s>>>24,this[o+2]=s>>>16,this[o+1]=s>>>8,this[o]=255&s,o+4},Buffer.prototype.writeUint32BE=Buffer.prototype.writeUInt32BE=function writeUInt32BE(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,4,4294967295,0),this[o]=s>>>24,this[o+1]=s>>>16,this[o+2]=s>>>8,this[o+3]=255&s,o+4},Buffer.prototype.writeBigUInt64LE=defineBigIntMethod((function writeBigUInt64LE(s,o=0){return wrtBigUInt64LE(this,s,o,BigInt(0),BigInt("0xffffffffffffffff"))})),Buffer.prototype.writeBigUInt64BE=defineBigIntMethod((function writeBigUInt64BE(s,o=0){return wrtBigUInt64BE(this,s,o,BigInt(0),BigInt("0xffffffffffffffff"))})),Buffer.prototype.writeIntLE=function writeIntLE(s,o,i,a){if(s=+s,o>>>=0,!a){const a=Math.pow(2,8*i-1);checkInt(this,s,o,i,a-1,-a)}let u=0,_=1,w=0;for(this[o]=255&s;++u>>=0,!a){const a=Math.pow(2,8*i-1);checkInt(this,s,o,i,a-1,-a)}let u=i-1,_=1,w=0;for(this[o+u]=255&s;--u>=0&&(_*=256);)s<0&&0===w&&0!==this[o+u+1]&&(w=1),this[o+u]=(s/_|0)-w&255;return o+i},Buffer.prototype.writeInt8=function writeInt8(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,1,127,-128),s<0&&(s=255+s+1),this[o]=255&s,o+1},Buffer.prototype.writeInt16LE=function writeInt16LE(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,2,32767,-32768),this[o]=255&s,this[o+1]=s>>>8,o+2},Buffer.prototype.writeInt16BE=function writeInt16BE(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,2,32767,-32768),this[o]=s>>>8,this[o+1]=255&s,o+2},Buffer.prototype.writeInt32LE=function writeInt32LE(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,4,2147483647,-2147483648),this[o]=255&s,this[o+1]=s>>>8,this[o+2]=s>>>16,this[o+3]=s>>>24,o+4},Buffer.prototype.writeInt32BE=function writeInt32BE(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,4,2147483647,-2147483648),s<0&&(s=4294967295+s+1),this[o]=s>>>24,this[o+1]=s>>>16,this[o+2]=s>>>8,this[o+3]=255&s,o+4},Buffer.prototype.writeBigInt64LE=defineBigIntMethod((function writeBigInt64LE(s,o=0){return wrtBigUInt64LE(this,s,o,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),Buffer.prototype.writeBigInt64BE=defineBigIntMethod((function writeBigInt64BE(s,o=0){return wrtBigUInt64BE(this,s,o,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),Buffer.prototype.writeFloatLE=function writeFloatLE(s,o,i){return writeFloat(this,s,o,!0,i)},Buffer.prototype.writeFloatBE=function writeFloatBE(s,o,i){return writeFloat(this,s,o,!1,i)},Buffer.prototype.writeDoubleLE=function writeDoubleLE(s,o,i){return writeDouble(this,s,o,!0,i)},Buffer.prototype.writeDoubleBE=function writeDoubleBE(s,o,i){return writeDouble(this,s,o,!1,i)},Buffer.prototype.copy=function copy(s,o,i,a){if(!Buffer.isBuffer(s))throw new TypeError("argument should be a Buffer");if(i||(i=0),a||0===a||(a=this.length),o>=s.length&&(o=s.length),o||(o=0),a>0&&a=this.length)throw new RangeError("Index out of range");if(a<0)throw new RangeError("sourceEnd out of bounds");a>this.length&&(a=this.length),s.length-o>>=0,i=void 0===i?this.length:i>>>0,s||(s=0),"number"==typeof s)for(u=o;u=a+4;i-=3)o=`_${s.slice(i-3,i)}${o}`;return`${s.slice(0,i)}${o}`}function checkIntBI(s,o,i,a,u,_){if(s>i||s3?0===o||o===BigInt(0)?`>= 0${a} and < 2${a} ** ${8*(_+1)}${a}`:`>= -(2${a} ** ${8*(_+1)-1}${a}) and < 2 ** ${8*(_+1)-1}${a}`:`>= ${o}${a} and <= ${i}${a}`,new C.ERR_OUT_OF_RANGE("value",u,s)}!function checkBounds(s,o,i){validateNumber(o,"offset"),void 0!==s[o]&&void 0!==s[o+i]||boundsError(o,s.length-(i+1))}(a,u,_)}function validateNumber(s,o){if("number"!=typeof s)throw new C.ERR_INVALID_ARG_TYPE(o,"number",s)}function boundsError(s,o,i){if(Math.floor(s)!==s)throw validateNumber(s,i),new C.ERR_OUT_OF_RANGE(i||"offset","an integer",s);if(o<0)throw new C.ERR_BUFFER_OUT_OF_BOUNDS;throw new C.ERR_OUT_OF_RANGE(i||"offset",`>= ${i?1:0} and <= ${o}`,s)}E("ERR_BUFFER_OUT_OF_BOUNDS",(function(s){return s?`${s} is outside of buffer bounds`:"Attempt to access memory outside buffer bounds"}),RangeError),E("ERR_INVALID_ARG_TYPE",(function(s,o){return`The "${s}" argument must be of type number. Received type ${typeof o}`}),TypeError),E("ERR_OUT_OF_RANGE",(function(s,o,i){let a=`The value of "${s}" is out of range.`,u=i;return Number.isInteger(i)&&Math.abs(i)>2**32?u=addNumericalSeparator(String(i)):"bigint"==typeof i&&(u=String(i),(i>BigInt(2)**BigInt(32)||i<-(BigInt(2)**BigInt(32)))&&(u=addNumericalSeparator(u)),u+="n"),a+=` It must be ${o}. Received ${u}`,a}),RangeError);const j=/[^+/0-9A-Za-z-_]/g;function utf8ToBytes(s,o){let i;o=o||1/0;const a=s.length;let u=null;const _=[];for(let w=0;w55295&&i<57344){if(!u){if(i>56319){(o-=3)>-1&&_.push(239,191,189);continue}if(w+1===a){(o-=3)>-1&&_.push(239,191,189);continue}u=i;continue}if(i<56320){(o-=3)>-1&&_.push(239,191,189),u=i;continue}i=65536+(u-55296<<10|i-56320)}else u&&(o-=3)>-1&&_.push(239,191,189);if(u=null,i<128){if((o-=1)<0)break;_.push(i)}else if(i<2048){if((o-=2)<0)break;_.push(i>>6|192,63&i|128)}else if(i<65536){if((o-=3)<0)break;_.push(i>>12|224,i>>6&63|128,63&i|128)}else{if(!(i<1114112))throw new Error("Invalid code point");if((o-=4)<0)break;_.push(i>>18|240,i>>12&63|128,i>>6&63|128,63&i|128)}}return _}function base64ToBytes(s){return a.toByteArray(function base64clean(s){if((s=(s=s.split("=")[0]).trim().replace(j,"")).length<2)return"";for(;s.length%4!=0;)s+="=";return s}(s))}function blitBuffer(s,o,i,a){let u;for(u=0;u=o.length||u>=s.length);++u)o[u+i]=s[u];return u}function isInstance(s,o){return s instanceof o||null!=s&&null!=s.constructor&&null!=s.constructor.name&&s.constructor.name===o.name}function numberIsNaN(s){return s!=s}const L=function(){const s="0123456789abcdef",o=new Array(256);for(let i=0;i<16;++i){const a=16*i;for(let u=0;u<16;++u)o[a+u]=s[i]+s[u]}return o}();function defineBigIntMethod(s){return"undefined"==typeof BigInt?BufferBigIntNotDefined:s}function BufferBigIntNotDefined(){throw new Error("BigInt not supported")}},13144(s,o,i){"use strict";var a=i(66743),u=i(11002),_=i(10076),w=i(47119);s.exports=w||a.call(_,u)},12205(s,o,i){"use strict";var a=i(66743),u=i(11002),_=i(13144);s.exports=function applyBind(){return _(a,u,arguments)}},11002(s){"use strict";s.exports=Function.prototype.apply},10076(s){"use strict";s.exports=Function.prototype.call},73126(s,o,i){"use strict";var a=i(66743),u=i(69675),_=i(10076),w=i(13144);s.exports=function callBindBasic(s){if(s.length<1||"function"!=typeof s[0])throw new u("a function is required");return w(a,_,s)}},47119(s){"use strict";s.exports="undefined"!=typeof Reflect&&Reflect&&Reflect.apply},10487(s,o,i){"use strict";var a=i(96897),u=i(30655),_=i(73126),w=i(12205);s.exports=function callBind(s){var o=_(arguments),i=s.length-(arguments.length-1);return a(o,1+(i>0?i:0),!0)},u?u(s.exports,"apply",{value:w}):s.exports.apply=w},36556(s,o,i){"use strict";var a=i(70453),u=i(73126),_=u([a("%String.prototype.indexOf%")]);s.exports=function callBoundIntrinsic(s,o){var i=a(s,!!o);return"function"==typeof i&&_(s,".prototype.")>-1?u([i]):i}},17965(s,o,i){"use strict";var a=i(16426),u={"text/plain":"Text","text/html":"Url",default:"Text"};s.exports=function copy(s,o){var i,_,w,x,C,j,L=!1;o||(o={}),i=o.debug||!1;try{if(w=a(),x=document.createRange(),C=document.getSelection(),(j=document.createElement("span")).textContent=s,j.ariaHidden="true",j.style.all="unset",j.style.position="fixed",j.style.top=0,j.style.clip="rect(0, 0, 0, 0)",j.style.whiteSpace="pre",j.style.webkitUserSelect="text",j.style.MozUserSelect="text",j.style.msUserSelect="text",j.style.userSelect="text",j.addEventListener("copy",(function(a){if(a.stopPropagation(),o.format)if(a.preventDefault(),void 0===a.clipboardData){i&&console.warn("unable to use e.clipboardData"),i&&console.warn("trying IE specific stuff"),window.clipboardData.clearData();var _=u[o.format]||u.default;window.clipboardData.setData(_,s)}else a.clipboardData.clearData(),a.clipboardData.setData(o.format,s);o.onCopy&&(a.preventDefault(),o.onCopy(a.clipboardData))})),document.body.appendChild(j),x.selectNodeContents(j),C.addRange(x),!document.execCommand("copy"))throw new Error("copy command was unsuccessful");L=!0}catch(a){i&&console.error("unable to copy using execCommand: ",a),i&&console.warn("trying IE specific stuff");try{window.clipboardData.setData(o.format||"text",s),o.onCopy&&o.onCopy(window.clipboardData),L=!0}catch(a){i&&console.error("unable to copy using clipboardData: ",a),i&&console.error("falling back to prompt"),_=function format(s){var o=(/mac os x/i.test(navigator.userAgent)?"⌘":"Ctrl")+"+C";return s.replace(/#{\s*key\s*}/g,o)}("message"in o?o.message:"Copy to clipboard: #{key}, Enter"),window.prompt(_,s)}}finally{C&&("function"==typeof C.removeRange?C.removeRange(x):C.removeAllRanges()),j&&document.body.removeChild(j),w()}return L}},2205(s,o,i){var a;a=void 0!==i.g?i.g:this,s.exports=function(s){if(s.CSS&&s.CSS.escape)return s.CSS.escape;var cssEscape=function(s){if(0==arguments.length)throw new TypeError("`CSS.escape` requires an argument.");for(var o,i=String(s),a=i.length,u=-1,_="",w=i.charCodeAt(0);++u=1&&o<=31||127==o||0==u&&o>=48&&o<=57||1==u&&o>=48&&o<=57&&45==w?"\\"+o.toString(16)+" ":0==u&&1==a&&45==o||!(o>=128||45==o||95==o||o>=48&&o<=57||o>=65&&o<=90||o>=97&&o<=122)?"\\"+i.charAt(u):i.charAt(u):_+="�";return _};return s.CSS||(s.CSS={}),s.CSS.escape=cssEscape,cssEscape}(a)},81919(s,o,i){"use strict";var a=i(48287).Buffer;function isSpecificValue(s){return s instanceof a||s instanceof Date||s instanceof RegExp}function cloneSpecificValue(s){if(s instanceof a){var o=a.alloc?a.alloc(s.length):new a(s.length);return s.copy(o),o}if(s instanceof Date)return new Date(s.getTime());if(s instanceof RegExp)return new RegExp(s);throw new Error("Unexpected situation")}function deepCloneArray(s){var o=[];return s.forEach((function(s,i){"object"==typeof s&&null!==s?Array.isArray(s)?o[i]=deepCloneArray(s):isSpecificValue(s)?o[i]=cloneSpecificValue(s):o[i]=u({},s):o[i]=s})),o}function safeGetProperty(s,o){return"__proto__"===o?void 0:s[o]}var u=s.exports=function(){if(arguments.length<1||"object"!=typeof arguments[0])return!1;if(arguments.length<2)return arguments[0];var s,o,i=arguments[0];return Array.prototype.slice.call(arguments,1).forEach((function(a){"object"!=typeof a||null===a||Array.isArray(a)||Object.keys(a).forEach((function(_){return o=safeGetProperty(i,_),(s=safeGetProperty(a,_))===i?void 0:"object"!=typeof s||null===s?void(i[_]=s):Array.isArray(s)?void(i[_]=deepCloneArray(s)):isSpecificValue(s)?void(i[_]=cloneSpecificValue(s)):"object"!=typeof o||null===o||Array.isArray(o)?void(i[_]=u({},s)):void(i[_]=u(o,s))}))})),i}},14744(s){"use strict";var o=function isMergeableObject(s){return function isNonNullObject(s){return!!s&&"object"==typeof s}(s)&&!function isSpecial(s){var o=Object.prototype.toString.call(s);return"[object RegExp]"===o||"[object Date]"===o||function isReactElement(s){return s.$$typeof===i}(s)}(s)};var i="function"==typeof Symbol&&Symbol.for?Symbol.for("react.element"):60103;function cloneUnlessOtherwiseSpecified(s,o){return!1!==o.clone&&o.isMergeableObject(s)?deepmerge(function emptyTarget(s){return Array.isArray(s)?[]:{}}(s),s,o):s}function defaultArrayMerge(s,o,i){return s.concat(o).map((function(s){return cloneUnlessOtherwiseSpecified(s,i)}))}function getKeys(s){return Object.keys(s).concat(function getEnumerableOwnPropertySymbols(s){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(s).filter((function(o){return Object.propertyIsEnumerable.call(s,o)})):[]}(s))}function propertyIsOnObject(s,o){try{return o in s}catch(s){return!1}}function mergeObject(s,o,i){var a={};return i.isMergeableObject(s)&&getKeys(s).forEach((function(o){a[o]=cloneUnlessOtherwiseSpecified(s[o],i)})),getKeys(o).forEach((function(u){(function propertyIsUnsafe(s,o){return propertyIsOnObject(s,o)&&!(Object.hasOwnProperty.call(s,o)&&Object.propertyIsEnumerable.call(s,o))})(s,u)||(propertyIsOnObject(s,u)&&i.isMergeableObject(o[u])?a[u]=function getMergeFunction(s,o){if(!o.customMerge)return deepmerge;var i=o.customMerge(s);return"function"==typeof i?i:deepmerge}(u,i)(s[u],o[u],i):a[u]=cloneUnlessOtherwiseSpecified(o[u],i))})),a}function deepmerge(s,i,a){(a=a||{}).arrayMerge=a.arrayMerge||defaultArrayMerge,a.isMergeableObject=a.isMergeableObject||o,a.cloneUnlessOtherwiseSpecified=cloneUnlessOtherwiseSpecified;var u=Array.isArray(i);return u===Array.isArray(s)?u?a.arrayMerge(s,i,a):mergeObject(s,i,a):cloneUnlessOtherwiseSpecified(i,a)}deepmerge.all=function deepmergeAll(s,o){if(!Array.isArray(s))throw new Error("first argument should be an array");return s.reduce((function(s,i){return deepmerge(s,i,o)}),{})};var a=deepmerge;s.exports=a},30041(s,o,i){"use strict";var a=i(30655),u=i(58068),_=i(69675),w=i(75795);s.exports=function defineDataProperty(s,o,i){if(!s||"object"!=typeof s&&"function"!=typeof s)throw new _("`obj` must be an object or a function`");if("string"!=typeof o&&"symbol"!=typeof o)throw new _("`property` must be a string or a symbol`");if(arguments.length>3&&"boolean"!=typeof arguments[3]&&null!==arguments[3])throw new _("`nonEnumerable`, if provided, must be a boolean or null");if(arguments.length>4&&"boolean"!=typeof arguments[4]&&null!==arguments[4])throw new _("`nonWritable`, if provided, must be a boolean or null");if(arguments.length>5&&"boolean"!=typeof arguments[5]&&null!==arguments[5])throw new _("`nonConfigurable`, if provided, must be a boolean or null");if(arguments.length>6&&"boolean"!=typeof arguments[6])throw new _("`loose`, if provided, must be a boolean");var x=arguments.length>3?arguments[3]:null,C=arguments.length>4?arguments[4]:null,j=arguments.length>5?arguments[5]:null,L=arguments.length>6&&arguments[6],B=!!w&&w(s,o);if(a)a(s,o,{configurable:null===j&&B?B.configurable:!j,enumerable:null===x&&B?B.enumerable:!x,value:i,writable:null===C&&B?B.writable:!C});else{if(!L&&(x||C||j))throw new u("This environment does not support defining a property as non-configurable, non-writable, or non-enumerable.");s[o]=i}}},78004(s){"use strict";class SubRange{constructor(s,o){this.low=s,this.high=o,this.length=1+o-s}overlaps(s){return!(this.highs.high)}touches(s){return!(this.high+1s.high)}add(s){return new SubRange(Math.min(this.low,s.low),Math.max(this.high,s.high))}subtract(s){return s.low<=this.low&&s.high>=this.high?[]:s.low>this.low&&s.highs+o.length),0)}add(s,o){var _add=s=>{for(var o=0;o{for(var o=0;o{for(var o=0;o{for(var i=o.low;i<=o.high;)s.push(i),i++;return s}),[])}subranges(){return this.ranges.map((s=>({low:s.low,high:s.high,length:1+s.high-s.low})))}}s.exports=DRange},7176(s,o,i){"use strict";var a,u=i(73126),_=i(75795);try{a=[].__proto__===Array.prototype}catch(s){if(!s||"object"!=typeof s||!("code"in s)||"ERR_PROTO_ACCESS"!==s.code)throw s}var w=!!a&&_&&_(Object.prototype,"__proto__"),x=Object,C=x.getPrototypeOf;s.exports=w&&"function"==typeof w.get?u([w.get]):"function"==typeof C&&function getDunder(s){return C(null==s?s:x(s))}},30655(s){"use strict";var o=Object.defineProperty||!1;if(o)try{o({},"a",{value:1})}catch(s){o=!1}s.exports=o},41237(s){"use strict";s.exports=EvalError},69383(s){"use strict";s.exports=Error},79290(s){"use strict";s.exports=RangeError},79538(s){"use strict";s.exports=ReferenceError},58068(s){"use strict";s.exports=SyntaxError},69675(s){"use strict";s.exports=TypeError},35345(s){"use strict";s.exports=URIError},79612(s){"use strict";s.exports=Object},37007(s){"use strict";var o,i="object"==typeof Reflect?Reflect:null,a=i&&"function"==typeof i.apply?i.apply:function ReflectApply(s,o,i){return Function.prototype.apply.call(s,o,i)};o=i&&"function"==typeof i.ownKeys?i.ownKeys:Object.getOwnPropertySymbols?function ReflectOwnKeys(s){return Object.getOwnPropertyNames(s).concat(Object.getOwnPropertySymbols(s))}:function ReflectOwnKeys(s){return Object.getOwnPropertyNames(s)};var u=Number.isNaN||function NumberIsNaN(s){return s!=s};function EventEmitter(){EventEmitter.init.call(this)}s.exports=EventEmitter,s.exports.once=function once(s,o){return new Promise((function(i,a){function errorListener(i){s.removeListener(o,resolver),a(i)}function resolver(){"function"==typeof s.removeListener&&s.removeListener("error",errorListener),i([].slice.call(arguments))}eventTargetAgnosticAddListener(s,o,resolver,{once:!0}),"error"!==o&&function addErrorHandlerIfEventEmitter(s,o,i){"function"==typeof s.on&&eventTargetAgnosticAddListener(s,"error",o,i)}(s,errorListener,{once:!0})}))},EventEmitter.EventEmitter=EventEmitter,EventEmitter.prototype._events=void 0,EventEmitter.prototype._eventsCount=0,EventEmitter.prototype._maxListeners=void 0;var _=10;function checkListener(s){if("function"!=typeof s)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof s)}function _getMaxListeners(s){return void 0===s._maxListeners?EventEmitter.defaultMaxListeners:s._maxListeners}function _addListener(s,o,i,a){var u,_,w;if(checkListener(i),void 0===(_=s._events)?(_=s._events=Object.create(null),s._eventsCount=0):(void 0!==_.newListener&&(s.emit("newListener",o,i.listener?i.listener:i),_=s._events),w=_[o]),void 0===w)w=_[o]=i,++s._eventsCount;else if("function"==typeof w?w=_[o]=a?[i,w]:[w,i]:a?w.unshift(i):w.push(i),(u=_getMaxListeners(s))>0&&w.length>u&&!w.warned){w.warned=!0;var x=new Error("Possible EventEmitter memory leak detected. "+w.length+" "+String(o)+" listeners added. Use emitter.setMaxListeners() to increase limit");x.name="MaxListenersExceededWarning",x.emitter=s,x.type=o,x.count=w.length,function ProcessEmitWarning(s){console&&console.warn&&console.warn(s)}(x)}return s}function onceWrapper(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function _onceWrap(s,o,i){var a={fired:!1,wrapFn:void 0,target:s,type:o,listener:i},u=onceWrapper.bind(a);return u.listener=i,a.wrapFn=u,u}function _listeners(s,o,i){var a=s._events;if(void 0===a)return[];var u=a[o];return void 0===u?[]:"function"==typeof u?i?[u.listener||u]:[u]:i?function unwrapListeners(s){for(var o=new Array(s.length),i=0;i0&&(w=o[0]),w instanceof Error)throw w;var x=new Error("Unhandled error."+(w?" ("+w.message+")":""));throw x.context=w,x}var C=_[s];if(void 0===C)return!1;if("function"==typeof C)a(C,this,o);else{var j=C.length,L=arrayClone(C,j);for(i=0;i=0;_--)if(i[_]===o||i[_].listener===o){w=i[_].listener,u=_;break}if(u<0)return this;0===u?i.shift():function spliceOne(s,o){for(;o+1=0;a--)this.removeListener(s,o[a]);return this},EventEmitter.prototype.listeners=function listeners(s){return _listeners(this,s,!0)},EventEmitter.prototype.rawListeners=function rawListeners(s){return _listeners(this,s,!1)},EventEmitter.listenerCount=function(s,o){return"function"==typeof s.listenerCount?s.listenerCount(o):listenerCount.call(s,o)},EventEmitter.prototype.listenerCount=listenerCount,EventEmitter.prototype.eventNames=function eventNames(){return this._eventsCount>0?o(this._events):[]}},85587(s,o,i){"use strict";var a=i(26311),u=create(Error);function create(s){return FormattedError.displayName=s.displayName||s.name,FormattedError;function FormattedError(o){return o&&(o=a.apply(null,arguments)),new s(o)}}s.exports=u,u.eval=create(EvalError),u.range=create(RangeError),u.reference=create(ReferenceError),u.syntax=create(SyntaxError),u.type=create(TypeError),u.uri=create(URIError),u.create=create},82682(s,o,i){"use strict";var a=i(69600),u=Object.prototype.toString,_=Object.prototype.hasOwnProperty;s.exports=function forEach(s,o,i){if(!a(o))throw new TypeError("iterator must be a function");var w;arguments.length>=3&&(w=i),function isArray(s){return"[object Array]"===u.call(s)}(s)?function forEachArray(s,o,i){for(var a=0,u=s.length;a0?parseInt(i):null};x1&&"boolean"!=typeof o)throw new L('"allowMissing" argument must be a boolean');if(null===Xe(/^%?[^%]*%?$/,s))throw new j("`%` may not be present anywhere but at the beginning and end of the intrinsic name");var i=function stringToPath(s){var o=Ye(s,0,1),i=Ye(s,-1);if("%"===o&&"%"!==i)throw new j("invalid intrinsic syntax, expected closing `%`");if("%"===i&&"%"!==o)throw new j("invalid intrinsic syntax, expected opening `%`");var a=[];return He(s,Qe,(function(s,o,i,u){a[a.length]=i?He(u,et,"$1"):o||s})),a}(s),a=i.length>0?i[0]:"",u=tt("%"+a+"%",o),_=u.name,w=u.value,x=!1,C=u.alias;C&&(a=C[0],We(i,ze([0,1],C)));for(var B=1,$=!0;B=i.length){var Y=ae(w,z);w=($=!!Y)&&"get"in Y&&!("originalValue"in Y.get)?Y.get:w[z]}else $=$e(w,z),w=w[z];$&&!x&&(xe[_]=w)}}return w}},71064(s,o,i){"use strict";var a=i(79612);s.exports=a.getPrototypeOf||null},48648(s){"use strict";s.exports="undefined"!=typeof Reflect&&Reflect.getPrototypeOf||null},93628(s,o,i){"use strict";var a=i(48648),u=i(71064),_=i(7176);s.exports=a?function getProto(s){return a(s)}:u?function getProto(s){if(!s||"object"!=typeof s&&"function"!=typeof s)throw new TypeError("getProto: not an object");return u(s)}:_?function getProto(s){return _(s)}:null},6549(s){"use strict";s.exports=Object.getOwnPropertyDescriptor},75795(s,o,i){"use strict";var a=i(6549);if(a)try{a([],"length")}catch(s){a=null}s.exports=a},30592(s,o,i){"use strict";var a=i(30655),u=function hasPropertyDescriptors(){return!!a};u.hasArrayLengthDefineBug=function hasArrayLengthDefineBug(){if(!a)return null;try{return 1!==a([],"length",{value:1}).length}catch(s){return!0}},s.exports=u},64039(s,o,i){"use strict";var a="undefined"!=typeof Symbol&&Symbol,u=i(41333);s.exports=function hasNativeSymbols(){return"function"==typeof a&&("function"==typeof Symbol&&("symbol"==typeof a("foo")&&("symbol"==typeof Symbol("bar")&&u())))}},41333(s){"use strict";s.exports=function hasSymbols(){if("function"!=typeof Symbol||"function"!=typeof Object.getOwnPropertySymbols)return!1;if("symbol"==typeof Symbol.iterator)return!0;var s={},o=Symbol("test"),i=Object(o);if("string"==typeof o)return!1;if("[object Symbol]"!==Object.prototype.toString.call(o))return!1;if("[object Symbol]"!==Object.prototype.toString.call(i))return!1;for(var a in s[o]=42,s)return!1;if("function"==typeof Object.keys&&0!==Object.keys(s).length)return!1;if("function"==typeof Object.getOwnPropertyNames&&0!==Object.getOwnPropertyNames(s).length)return!1;var u=Object.getOwnPropertySymbols(s);if(1!==u.length||u[0]!==o)return!1;if(!Object.prototype.propertyIsEnumerable.call(s,o))return!1;if("function"==typeof Object.getOwnPropertyDescriptor){var _=Object.getOwnPropertyDescriptor(s,o);if(42!==_.value||!0!==_.enumerable)return!1}return!0}},49092(s,o,i){"use strict";var a=i(41333);s.exports=function hasToStringTagShams(){return a()&&!!Symbol.toStringTag}},9957(s,o,i){"use strict";var a=Function.prototype.call,u=Object.prototype.hasOwnProperty,_=i(66743);s.exports=_.call(a,u)},45981(s){function deepFreeze(s){return s instanceof Map?s.clear=s.delete=s.set=function(){throw new Error("map is read-only")}:s instanceof Set&&(s.add=s.clear=s.delete=function(){throw new Error("set is read-only")}),Object.freeze(s),Object.getOwnPropertyNames(s).forEach((function(o){var i=s[o];"object"!=typeof i||Object.isFrozen(i)||deepFreeze(i)})),s}var o=deepFreeze,i=deepFreeze;o.default=i;class Response{constructor(s){void 0===s.data&&(s.data={}),this.data=s.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function escapeHTML(s){return s.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function inherit(s,...o){const i=Object.create(null);for(const o in s)i[o]=s[o];return o.forEach((function(s){for(const o in s)i[o]=s[o]})),i}const emitsWrappingTags=s=>!!s.kind;class HTMLRenderer{constructor(s,o){this.buffer="",this.classPrefix=o.classPrefix,s.walk(this)}addText(s){this.buffer+=escapeHTML(s)}openNode(s){if(!emitsWrappingTags(s))return;let o=s.kind;s.sublanguage||(o=`${this.classPrefix}${o}`),this.span(o)}closeNode(s){emitsWrappingTags(s)&&(this.buffer+="")}value(){return this.buffer}span(s){this.buffer+=``}}class TokenTree{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(s){this.top.children.push(s)}openNode(s){const o={kind:s,children:[]};this.add(o),this.stack.push(o)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(s){return this.constructor._walk(s,this.rootNode)}static _walk(s,o){return"string"==typeof o?s.addText(o):o.children&&(s.openNode(o),o.children.forEach((o=>this._walk(s,o))),s.closeNode(o)),s}static _collapse(s){"string"!=typeof s&&s.children&&(s.children.every((s=>"string"==typeof s))?s.children=[s.children.join("")]:s.children.forEach((s=>{TokenTree._collapse(s)})))}}class TokenTreeEmitter extends TokenTree{constructor(s){super(),this.options=s}addKeyword(s,o){""!==s&&(this.openNode(o),this.addText(s),this.closeNode())}addText(s){""!==s&&this.add(s)}addSublanguage(s,o){const i=s.root;i.kind=o,i.sublanguage=!0,this.add(i)}toHTML(){return new HTMLRenderer(this,this.options).value()}finalize(){return!0}}function source(s){return s?"string"==typeof s?s:s.source:null}const a=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;const u="[a-zA-Z]\\w*",_="[a-zA-Z_]\\w*",w="\\b\\d+(\\.\\d+)?",x="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",C="\\b(0b[01]+)",j={begin:"\\\\[\\s\\S]",relevance:0},L={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[j]},B={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[j]},$={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},COMMENT=function(s,o,i={}){const a=inherit({className:"comment",begin:s,end:o,contains:[]},i);return a.contains.push($),a.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),a},z=COMMENT("//","$"),U=COMMENT("/\\*","\\*/"),V=COMMENT("#","$"),Y={className:"number",begin:w,relevance:0},Z={className:"number",begin:x,relevance:0},ee={className:"number",begin:C,relevance:0},ie={className:"number",begin:w+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},ae={begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[j,{begin:/\[/,end:/\]/,relevance:0,contains:[j]}]}]},ce={className:"title",begin:u,relevance:0},le={className:"title",begin:_,relevance:0},pe={begin:"\\.\\s*"+_,relevance:0};var de=Object.freeze({__proto__:null,MATCH_NOTHING_RE:/\b\B/,IDENT_RE:u,UNDERSCORE_IDENT_RE:_,NUMBER_RE:w,C_NUMBER_RE:x,BINARY_NUMBER_RE:C,RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(s={})=>{const o=/^#![ ]*\//;return s.binary&&(s.begin=function concat(...s){return s.map((s=>source(s))).join("")}(o,/.*\b/,s.binary,/\b.*/)),inherit({className:"meta",begin:o,end:/$/,relevance:0,"on:begin":(s,o)=>{0!==s.index&&o.ignoreMatch()}},s)},BACKSLASH_ESCAPE:j,APOS_STRING_MODE:L,QUOTE_STRING_MODE:B,PHRASAL_WORDS_MODE:$,COMMENT,C_LINE_COMMENT_MODE:z,C_BLOCK_COMMENT_MODE:U,HASH_COMMENT_MODE:V,NUMBER_MODE:Y,C_NUMBER_MODE:Z,BINARY_NUMBER_MODE:ee,CSS_NUMBER_MODE:ie,REGEXP_MODE:ae,TITLE_MODE:ce,UNDERSCORE_TITLE_MODE:le,METHOD_GUARD:pe,END_SAME_AS_BEGIN:function(s){return Object.assign(s,{"on:begin":(s,o)=>{o.data._beginMatch=s[1]},"on:end":(s,o)=>{o.data._beginMatch!==s[1]&&o.ignoreMatch()}})}});function skipIfhasPrecedingDot(s,o){"."===s.input[s.index-1]&&o.ignoreMatch()}function beginKeywords(s,o){o&&s.beginKeywords&&(s.begin="\\b("+s.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",s.__beforeBegin=skipIfhasPrecedingDot,s.keywords=s.keywords||s.beginKeywords,delete s.beginKeywords,void 0===s.relevance&&(s.relevance=0))}function compileIllegal(s,o){Array.isArray(s.illegal)&&(s.illegal=function either(...s){return"("+s.map((s=>source(s))).join("|")+")"}(...s.illegal))}function compileMatch(s,o){if(s.match){if(s.begin||s.end)throw new Error("begin & end are not supported with match");s.begin=s.match,delete s.match}}function compileRelevance(s,o){void 0===s.relevance&&(s.relevance=1)}const fe=["of","and","for","in","not","or","if","then","parent","list","value"];function compileKeywords(s,o,i="keyword"){const a={};return"string"==typeof s?compileList(i,s.split(" ")):Array.isArray(s)?compileList(i,s):Object.keys(s).forEach((function(i){Object.assign(a,compileKeywords(s[i],o,i))})),a;function compileList(s,i){o&&(i=i.map((s=>s.toLowerCase()))),i.forEach((function(o){const i=o.split("|");a[i[0]]=[s,scoreForKeyword(i[0],i[1])]}))}}function scoreForKeyword(s,o){return o?Number(o):function commonKeyword(s){return fe.includes(s.toLowerCase())}(s)?0:1}function compileLanguage(s,{plugins:o}){function langRe(o,i){return new RegExp(source(o),"m"+(s.case_insensitive?"i":"")+(i?"g":""))}class MultiRegex{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(s,o){o.position=this.position++,this.matchIndexes[this.matchAt]=o,this.regexes.push([o,s]),this.matchAt+=function countMatchGroups(s){return new RegExp(s.toString()+"|").exec("").length-1}(s)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const s=this.regexes.map((s=>s[1]));this.matcherRe=langRe(function join(s,o="|"){let i=0;return s.map((s=>{i+=1;const o=i;let u=source(s),_="";for(;u.length>0;){const s=a.exec(u);if(!s){_+=u;break}_+=u.substring(0,s.index),u=u.substring(s.index+s[0].length),"\\"===s[0][0]&&s[1]?_+="\\"+String(Number(s[1])+o):(_+=s[0],"("===s[0]&&i++)}return _})).map((s=>`(${s})`)).join(o)}(s),!0),this.lastIndex=0}exec(s){this.matcherRe.lastIndex=this.lastIndex;const o=this.matcherRe.exec(s);if(!o)return null;const i=o.findIndex(((s,o)=>o>0&&void 0!==s)),a=this.matchIndexes[i];return o.splice(0,i),Object.assign(o,a)}}class ResumableMultiRegex{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(s){if(this.multiRegexes[s])return this.multiRegexes[s];const o=new MultiRegex;return this.rules.slice(s).forEach((([s,i])=>o.addRule(s,i))),o.compile(),this.multiRegexes[s]=o,o}resumingScanAtSamePosition(){return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(s,o){this.rules.push([s,o]),"begin"===o.type&&this.count++}exec(s){const o=this.getMatcher(this.regexIndex);o.lastIndex=this.lastIndex;let i=o.exec(s);if(this.resumingScanAtSamePosition())if(i&&i.index===this.lastIndex);else{const o=this.getMatcher(0);o.lastIndex=this.lastIndex+1,i=o.exec(s)}return i&&(this.regexIndex+=i.position+1,this.regexIndex===this.count&&this.considerAll()),i}}if(s.compilerExtensions||(s.compilerExtensions=[]),s.contains&&s.contains.includes("self"))throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return s.classNameAliases=inherit(s.classNameAliases||{}),function compileMode(o,i){const a=o;if(o.isCompiled)return a;[compileMatch].forEach((s=>s(o,i))),s.compilerExtensions.forEach((s=>s(o,i))),o.__beforeBegin=null,[beginKeywords,compileIllegal,compileRelevance].forEach((s=>s(o,i))),o.isCompiled=!0;let u=null;if("object"==typeof o.keywords&&(u=o.keywords.$pattern,delete o.keywords.$pattern),o.keywords&&(o.keywords=compileKeywords(o.keywords,s.case_insensitive)),o.lexemes&&u)throw new Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");return u=u||o.lexemes||/\w+/,a.keywordPatternRe=langRe(u,!0),i&&(o.begin||(o.begin=/\B|\b/),a.beginRe=langRe(o.begin),o.endSameAsBegin&&(o.end=o.begin),o.end||o.endsWithParent||(o.end=/\B|\b/),o.end&&(a.endRe=langRe(o.end)),a.terminatorEnd=source(o.end)||"",o.endsWithParent&&i.terminatorEnd&&(a.terminatorEnd+=(o.end?"|":"")+i.terminatorEnd)),o.illegal&&(a.illegalRe=langRe(o.illegal)),o.contains||(o.contains=[]),o.contains=[].concat(...o.contains.map((function(s){return function expandOrCloneMode(s){s.variants&&!s.cachedVariants&&(s.cachedVariants=s.variants.map((function(o){return inherit(s,{variants:null},o)})));if(s.cachedVariants)return s.cachedVariants;if(dependencyOnParent(s))return inherit(s,{starts:s.starts?inherit(s.starts):null});if(Object.isFrozen(s))return inherit(s);return s}("self"===s?o:s)}))),o.contains.forEach((function(s){compileMode(s,a)})),o.starts&&compileMode(o.starts,i),a.matcher=function buildModeRegex(s){const o=new ResumableMultiRegex;return s.contains.forEach((s=>o.addRule(s.begin,{rule:s,type:"begin"}))),s.terminatorEnd&&o.addRule(s.terminatorEnd,{type:"end"}),s.illegal&&o.addRule(s.illegal,{type:"illegal"}),o}(a),a}(s)}function dependencyOnParent(s){return!!s&&(s.endsWithParent||dependencyOnParent(s.starts))}function BuildVuePlugin(s){const o={props:["language","code","autodetect"],data:function(){return{detectedLanguage:"",unknownLanguage:!1}},computed:{className(){return this.unknownLanguage?"":"hljs "+this.detectedLanguage},highlighted(){if(!this.autoDetect&&!s.getLanguage(this.language))return console.warn(`The language "${this.language}" you specified could not be found.`),this.unknownLanguage=!0,escapeHTML(this.code);let o={};return this.autoDetect?(o=s.highlightAuto(this.code),this.detectedLanguage=o.language):(o=s.highlight(this.language,this.code,this.ignoreIllegals),this.detectedLanguage=this.language),o.value},autoDetect(){return!this.language||function hasValueOrEmptyAttribute(s){return Boolean(s||""===s)}(this.autodetect)},ignoreIllegals:()=>!0},render(s){return s("pre",{},[s("code",{class:this.className,domProps:{innerHTML:this.highlighted}})])}};return{Component:o,VuePlugin:{install(s){s.component("highlightjs",o)}}}}const ye={"after:highlightElement":({el:s,result:o,text:i})=>{const a=nodeStream(s);if(!a.length)return;const u=document.createElement("div");u.innerHTML=o.value,o.value=function mergeStreams(s,o,i){let a=0,u="";const _=[];function selectStream(){return s.length&&o.length?s[0].offset!==o[0].offset?s[0].offset"}function close(s){u+=""}function render(s){("start"===s.event?open:close)(s.node)}for(;s.length||o.length;){let o=selectStream();if(u+=escapeHTML(i.substring(a,o[0].offset)),a=o[0].offset,o===s){_.reverse().forEach(close);do{render(o.splice(0,1)[0]),o=selectStream()}while(o===s&&o.length&&o[0].offset===a);_.reverse().forEach(open)}else"start"===o[0].event?_.push(o[0].node):_.pop(),render(o.splice(0,1)[0])}return u+escapeHTML(i.substr(a))}(a,nodeStream(u),i)}};function tag(s){return s.nodeName.toLowerCase()}function nodeStream(s){const o=[];return function _nodeStream(s,i){for(let a=s.firstChild;a;a=a.nextSibling)3===a.nodeType?i+=a.nodeValue.length:1===a.nodeType&&(o.push({event:"start",offset:i,node:a}),i=_nodeStream(a,i),tag(a).match(/br|hr|img|input/)||o.push({event:"stop",offset:i,node:a}));return i}(s,0),o}const be={},error=s=>{console.error(s)},warn=(s,...o)=>{console.log(`WARN: ${s}`,...o)},deprecated=(s,o)=>{be[`${s}/${o}`]||(console.log(`Deprecated as of ${s}. ${o}`),be[`${s}/${o}`]=!0)},Se=escapeHTML,_e=inherit,we=Symbol("nomatch");var xe=function(s){const i=Object.create(null),a=Object.create(null),u=[];let _=!0;const w=/(^(<[^>]+>|\t|)+|\n)/gm,x="Could not find the language '{}', did you forget to load/include a language module?",C={disableAutodetect:!0,name:"Plain text",contains:[]};let j={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:null,__emitter:TokenTreeEmitter};function shouldNotHighlight(s){return j.noHighlightRe.test(s)}function highlight(s,o,i,a){let u="",_="";"object"==typeof o?(u=s,i=o.ignoreIllegals,_=o.language,a=void 0):(deprecated("10.7.0","highlight(lang, code, ...args) has been deprecated."),deprecated("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),_=s,u=o);const w={code:u,language:_};fire("before:highlight",w);const x=w.result?w.result:_highlight(w.language,w.code,i,a);return x.code=w.code,fire("after:highlight",x),x}function _highlight(s,o,a,w){function keywordData(s,o){const i=L.case_insensitive?o[0].toLowerCase():o[0];return Object.prototype.hasOwnProperty.call(s.keywords,i)&&s.keywords[i]}function processBuffer(){null!=z.subLanguage?function processSubLanguage(){if(""===Y)return;let s=null;if("string"==typeof z.subLanguage){if(!i[z.subLanguage])return void V.addText(Y);s=_highlight(z.subLanguage,Y,!0,U[z.subLanguage]),U[z.subLanguage]=s.top}else s=highlightAuto(Y,z.subLanguage.length?z.subLanguage:null);z.relevance>0&&(Z+=s.relevance),V.addSublanguage(s.emitter,s.language)}():function processKeywords(){if(!z.keywords)return void V.addText(Y);let s=0;z.keywordPatternRe.lastIndex=0;let o=z.keywordPatternRe.exec(Y),i="";for(;o;){i+=Y.substring(s,o.index);const a=keywordData(z,o);if(a){const[s,u]=a;if(V.addText(i),i="",Z+=u,s.startsWith("_"))i+=o[0];else{const i=L.classNameAliases[s]||s;V.addKeyword(o[0],i)}}else i+=o[0];s=z.keywordPatternRe.lastIndex,o=z.keywordPatternRe.exec(Y)}i+=Y.substr(s),V.addText(i)}(),Y=""}function startNewMode(s){return s.className&&V.openNode(L.classNameAliases[s.className]||s.className),z=Object.create(s,{parent:{value:z}}),z}function endOfMode(s,o,i){let a=function startsWith(s,o){const i=s&&s.exec(o);return i&&0===i.index}(s.endRe,i);if(a){if(s["on:end"]){const i=new Response(s);s["on:end"](o,i),i.isMatchIgnored&&(a=!1)}if(a){for(;s.endsParent&&s.parent;)s=s.parent;return s}}if(s.endsWithParent)return endOfMode(s.parent,o,i)}function doIgnore(s){return 0===z.matcher.regexIndex?(Y+=s[0],1):(ae=!0,0)}function doBeginMatch(s){const o=s[0],i=s.rule,a=new Response(i),u=[i.__beforeBegin,i["on:begin"]];for(const i of u)if(i&&(i(s,a),a.isMatchIgnored))return doIgnore(o);return i&&i.endSameAsBegin&&(i.endRe=function escape(s){return new RegExp(s.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")}(o)),i.skip?Y+=o:(i.excludeBegin&&(Y+=o),processBuffer(),i.returnBegin||i.excludeBegin||(Y=o)),startNewMode(i),i.returnBegin?0:o.length}function doEndMatch(s){const i=s[0],a=o.substr(s.index),u=endOfMode(z,s,a);if(!u)return we;const _=z;_.skip?Y+=i:(_.returnEnd||_.excludeEnd||(Y+=i),processBuffer(),_.excludeEnd&&(Y=i));do{z.className&&V.closeNode(),z.skip||z.subLanguage||(Z+=z.relevance),z=z.parent}while(z!==u.parent);return u.starts&&(u.endSameAsBegin&&(u.starts.endRe=u.endRe),startNewMode(u.starts)),_.returnEnd?0:i.length}let C={};function processLexeme(i,u){const w=u&&u[0];if(Y+=i,null==w)return processBuffer(),0;if("begin"===C.type&&"end"===u.type&&C.index===u.index&&""===w){if(Y+=o.slice(u.index,u.index+1),!_){const o=new Error("0 width match regex");throw o.languageName=s,o.badRule=C.rule,o}return 1}if(C=u,"begin"===u.type)return doBeginMatch(u);if("illegal"===u.type&&!a){const s=new Error('Illegal lexeme "'+w+'" for mode "'+(z.className||"")+'"');throw s.mode=z,s}if("end"===u.type){const s=doEndMatch(u);if(s!==we)return s}if("illegal"===u.type&&""===w)return 1;if(ie>1e5&&ie>3*u.index){throw new Error("potential infinite loop, way more iterations than matches")}return Y+=w,w.length}const L=getLanguage(s);if(!L)throw error(x.replace("{}",s)),new Error('Unknown language: "'+s+'"');const B=compileLanguage(L,{plugins:u});let $="",z=w||B;const U={},V=new j.__emitter(j);!function processContinuations(){const s=[];for(let o=z;o!==L;o=o.parent)o.className&&s.unshift(o.className);s.forEach((s=>V.openNode(s)))}();let Y="",Z=0,ee=0,ie=0,ae=!1;try{for(z.matcher.considerAll();;){ie++,ae?ae=!1:z.matcher.considerAll(),z.matcher.lastIndex=ee;const s=z.matcher.exec(o);if(!s)break;const i=processLexeme(o.substring(ee,s.index),s);ee=s.index+i}return processLexeme(o.substr(ee)),V.closeAllNodes(),V.finalize(),$=V.toHTML(),{relevance:Math.floor(Z),value:$,language:s,illegal:!1,emitter:V,top:z}}catch(i){if(i.message&&i.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:i.message,context:o.slice(ee-100,ee+100),mode:i.mode},sofar:$,relevance:0,value:Se(o),emitter:V};if(_)return{illegal:!1,relevance:0,value:Se(o),emitter:V,language:s,top:z,errorRaised:i};throw i}}function highlightAuto(s,o){o=o||j.languages||Object.keys(i);const a=function justTextHighlightResult(s){const o={relevance:0,emitter:new j.__emitter(j),value:Se(s),illegal:!1,top:C};return o.emitter.addText(s),o}(s),u=o.filter(getLanguage).filter(autoDetection).map((o=>_highlight(o,s,!1)));u.unshift(a);const _=u.sort(((s,o)=>{if(s.relevance!==o.relevance)return o.relevance-s.relevance;if(s.language&&o.language){if(getLanguage(s.language).supersetOf===o.language)return 1;if(getLanguage(o.language).supersetOf===s.language)return-1}return 0})),[w,x]=_,L=w;return L.second_best=x,L}const L={"before:highlightElement":({el:s})=>{j.useBR&&(s.innerHTML=s.innerHTML.replace(/\n/g,"").replace(//g,"\n"))},"after:highlightElement":({result:s})=>{j.useBR&&(s.value=s.value.replace(/\n/g,"
"))}},B=/^(<[^>]+>|\t)+/gm,$={"after:highlightElement":({result:s})=>{j.tabReplace&&(s.value=s.value.replace(B,(s=>s.replace(/\t/g,j.tabReplace))))}};function highlightElement(s){let o=null;const i=function blockLanguage(s){let o=s.className+" ";o+=s.parentNode?s.parentNode.className:"";const i=j.languageDetectRe.exec(o);if(i){const o=getLanguage(i[1]);return o||(warn(x.replace("{}",i[1])),warn("Falling back to no-highlight mode for this block.",s)),o?i[1]:"no-highlight"}return o.split(/\s+/).find((s=>shouldNotHighlight(s)||getLanguage(s)))}(s);if(shouldNotHighlight(i))return;fire("before:highlightElement",{el:s,language:i}),o=s;const u=o.textContent,_=i?highlight(u,{language:i,ignoreIllegals:!0}):highlightAuto(u);fire("after:highlightElement",{el:s,result:_,text:u}),s.innerHTML=_.value,function updateClassName(s,o,i){const u=o?a[o]:i;s.classList.add("hljs"),u&&s.classList.add(u)}(s,i,_.language),s.result={language:_.language,re:_.relevance,relavance:_.relevance},_.second_best&&(s.second_best={language:_.second_best.language,re:_.second_best.relevance,relavance:_.second_best.relevance})}const initHighlighting=()=>{if(initHighlighting.called)return;initHighlighting.called=!0,deprecated("10.6.0","initHighlighting() is deprecated. Use highlightAll() instead.");document.querySelectorAll("pre code").forEach(highlightElement)};let z=!1;function highlightAll(){if("loading"===document.readyState)return void(z=!0);document.querySelectorAll("pre code").forEach(highlightElement)}function getLanguage(s){return s=(s||"").toLowerCase(),i[s]||i[a[s]]}function registerAliases(s,{languageName:o}){"string"==typeof s&&(s=[s]),s.forEach((s=>{a[s.toLowerCase()]=o}))}function autoDetection(s){const o=getLanguage(s);return o&&!o.disableAutodetect}function fire(s,o){const i=s;u.forEach((function(s){s[i]&&s[i](o)}))}"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(function boot(){z&&highlightAll()}),!1),Object.assign(s,{highlight,highlightAuto,highlightAll,fixMarkup:function deprecateFixMarkup(s){return deprecated("10.2.0","fixMarkup will be removed entirely in v11.0"),deprecated("10.2.0","Please see https://github.com/highlightjs/highlight.js/issues/2534"),function fixMarkup(s){return j.tabReplace||j.useBR?s.replace(w,(s=>"\n"===s?j.useBR?"
":s:j.tabReplace?s.replace(/\t/g,j.tabReplace):s)):s}(s)},highlightElement,highlightBlock:function deprecateHighlightBlock(s){return deprecated("10.7.0","highlightBlock will be removed entirely in v12.0"),deprecated("10.7.0","Please use highlightElement now."),highlightElement(s)},configure:function configure(s){s.useBR&&(deprecated("10.3.0","'useBR' will be removed entirely in v11.0"),deprecated("10.3.0","Please see https://github.com/highlightjs/highlight.js/issues/2559")),j=_e(j,s)},initHighlighting,initHighlightingOnLoad:function initHighlightingOnLoad(){deprecated("10.6.0","initHighlightingOnLoad() is deprecated. Use highlightAll() instead."),z=!0},registerLanguage:function registerLanguage(o,a){let u=null;try{u=a(s)}catch(s){if(error("Language definition for '{}' could not be registered.".replace("{}",o)),!_)throw s;error(s),u=C}u.name||(u.name=o),i[o]=u,u.rawDefinition=a.bind(null,s),u.aliases&®isterAliases(u.aliases,{languageName:o})},unregisterLanguage:function unregisterLanguage(s){delete i[s];for(const o of Object.keys(a))a[o]===s&&delete a[o]},listLanguages:function listLanguages(){return Object.keys(i)},getLanguage,registerAliases,requireLanguage:function requireLanguage(s){deprecated("10.4.0","requireLanguage will be removed entirely in v11."),deprecated("10.4.0","Please see https://github.com/highlightjs/highlight.js/pull/2844");const o=getLanguage(s);if(o)return o;throw new Error("The '{}' language is required, but not loaded.".replace("{}",s))},autoDetection,inherit:_e,addPlugin:function addPlugin(s){!function upgradePluginAPI(s){s["before:highlightBlock"]&&!s["before:highlightElement"]&&(s["before:highlightElement"]=o=>{s["before:highlightBlock"](Object.assign({block:o.el},o))}),s["after:highlightBlock"]&&!s["after:highlightElement"]&&(s["after:highlightElement"]=o=>{s["after:highlightBlock"](Object.assign({block:o.el},o))})}(s),u.push(s)},vuePlugin:BuildVuePlugin(s).VuePlugin}),s.debugMode=function(){_=!1},s.safeMode=function(){_=!0},s.versionString="10.7.3";for(const s in de)"object"==typeof de[s]&&o(de[s]);return Object.assign(s,de),s.addPlugin(L),s.addPlugin(ye),s.addPlugin($),s}({});s.exports=xe},35344(s){function concat(...s){return s.map((s=>function source(s){return s?"string"==typeof s?s:s.source:null}(s))).join("")}s.exports=function bash(s){const o={},i={begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[o]}]};Object.assign(o,{className:"variable",variants:[{begin:concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},i]});const a={className:"subst",begin:/\$\(/,end:/\)/,contains:[s.BACKSLASH_ESCAPE]},u={begin:/<<-?\s*(?=\w+)/,starts:{contains:[s.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,className:"string"})]}},_={className:"string",begin:/"/,end:/"/,contains:[s.BACKSLASH_ESCAPE,o,a]};a.contains.push(_);const w={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},s.NUMBER_MODE,o]},x=s.SHEBANG({binary:`(${["fish","bash","zsh","sh","csh","ksh","tcsh","dash","scsh"].join("|")})`,relevance:10}),C={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[s.inherit(s.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b[a-z._-]+\b/,keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp"},contains:[x,s.SHEBANG(),C,w,s.HASH_COMMENT_MODE,u,_,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},o]}}},73402(s){function concat(...s){return s.map((s=>function source(s){return s?"string"==typeof s?s:s.source:null}(s))).join("")}s.exports=function http(s){const o="HTTP/(2|1\\.[01])",i={className:"attribute",begin:concat("^",/[A-Za-z][A-Za-z0-9-]*/,"(?=\\:\\s)"),starts:{contains:[{className:"punctuation",begin:/: /,relevance:0,starts:{end:"$",relevance:0}}]}},a=[i,{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}];return{name:"HTTP",aliases:["https"],illegal:/\S/,contains:[{begin:"^(?="+o+" \\d{3})",end:/$/,contains:[{className:"meta",begin:o},{className:"number",begin:"\\b\\d{3}\\b"}],starts:{end:/\b\B/,illegal:/\S/,contains:a}},{begin:"(?=^[A-Z]+ (.*?) "+o+"$)",end:/$/,contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{className:"meta",begin:o},{className:"keyword",begin:"[A-Z]+"}],starts:{end:/\b\B/,illegal:/\S/,contains:a}},s.inherit(i,{relevance:0})]}}},95089(s){const o="[A-Za-z$_][0-9A-Za-z$_]*",i=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],u=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer","BigInt64Array","BigUint64Array","BigInt"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);function lookahead(s){return concat("(?=",s,")")}function concat(...s){return s.map((s=>function source(s){return s?"string"==typeof s?s:s.source:null}(s))).join("")}s.exports=function javascript(s){const _=o,w="<>",x="",C={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(s,o)=>{const i=s[0].length+s.index,a=s.input[i];"<"!==a?">"===a&&(((s,{after:o})=>{const i="",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:s.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:j,contains:ce}]}]},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{variants:[{begin:w,end:x},{begin:C.begin,"on:begin":C.isTrulyOpeningTag,end:C.end}],subLanguage:"xml",contains:[{begin:C.begin,end:C.end,skip:!0,contains:["self"]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/[{;]/,excludeEnd:!0,keywords:j,contains:["self",s.inherit(s.TITLE_MODE,{begin:_}),le],illegal:/%/},{beginKeywords:"while if switch catch for"},{className:"function",begin:s.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",returnBegin:!0,contains:[le,s.inherit(s.TITLE_MODE,{begin:_})]},{variants:[{begin:"\\."+_},{begin:"\\$"+_}],relevance:0},{className:"class",beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"[\]]/,contains:[{beginKeywords:"extends"},s.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/,end:/[{;]/,excludeEnd:!0,contains:[s.inherit(s.TITLE_MODE,{begin:_}),"self",le]},{begin:"(get|set)\\s+(?="+_+"\\()",end:/\{/,keywords:"get set",contains:[s.inherit(s.TITLE_MODE,{begin:_}),{begin:/\(\)/},le]},{begin:/\$[(.]/}]}}},65772(s){s.exports=function json(s){const o={literal:"true false null"},i=[s.C_LINE_COMMENT_MODE,s.C_BLOCK_COMMENT_MODE],a=[s.QUOTE_STRING_MODE,s.C_NUMBER_MODE],u={end:",",endsWithParent:!0,excludeEnd:!0,contains:a,keywords:o},_={begin:/\{/,end:/\}/,contains:[{className:"attr",begin:/"/,end:/"/,contains:[s.BACKSLASH_ESCAPE],illegal:"\\n"},s.inherit(u,{begin:/:/})].concat(i),illegal:"\\S"},w={begin:"\\[",end:"\\]",contains:[s.inherit(u)],illegal:"\\S"};return a.push(_,w),i.forEach((function(s){a.push(s)})),{name:"JSON",contains:a,keywords:o,illegal:"\\S"}}},26571(s){s.exports=function powershell(s){const o={$pattern:/-?[A-z\.\-]+\b/,keyword:"if else foreach return do while until elseif begin for trap data dynamicparam end break throw param continue finally in switch exit filter try process catch hidden static parameter",built_in:"ac asnp cat cd CFS chdir clc clear clhy cli clp cls clv cnsn compare copy cp cpi cpp curl cvpa dbp del diff dir dnsn ebp echo|0 epal epcsv epsn erase etsn exsn fc fhx fl ft fw gal gbp gc gcb gci gcm gcs gdr gerr ghy gi gin gjb gl gm gmo gp gps gpv group gsn gsnp gsv gtz gu gv gwmi h history icm iex ihy ii ipal ipcsv ipmo ipsn irm ise iwmi iwr kill lp ls man md measure mi mount move mp mv nal ndr ni nmo npssc nsn nv ogv oh popd ps pushd pwd r rbp rcjb rcsn rd rdr ren ri rjb rm rmdir rmo rni rnp rp rsn rsnp rujb rv rvpa rwmi sajb sal saps sasv sbp sc scb select set shcm si sl sleep sls sort sp spjb spps spsv start stz sujb sv swmi tee trcm type wget where wjb write"},i={begin:"`[\\s\\S]",relevance:0},a={className:"variable",variants:[{begin:/\$\B/},{className:"keyword",begin:/\$this/},{begin:/\$[\w\d][\w\d_:]*/}]},u={className:"string",variants:[{begin:/"/,end:/"/},{begin:/@"/,end:/^"@/}],contains:[i,a,{className:"variable",begin:/\$[A-z]/,end:/[^A-z]/}]},_={className:"string",variants:[{begin:/'/,end:/'/},{begin:/@'/,end:/^'@/}]},w=s.inherit(s.COMMENT(null,null),{variants:[{begin:/#/,end:/$/},{begin:/<#/,end:/#>/}],contains:[{className:"doctag",variants:[{begin:/\.(synopsis|description|example|inputs|outputs|notes|link|component|role|functionality)/},{begin:/\.(parameter|forwardhelptargetname|forwardhelpcategory|remotehelprunspace|externalhelp)\s+\S+/}]}]}),x={className:"built_in",variants:[{begin:"(".concat("Add|Clear|Close|Copy|Enter|Exit|Find|Format|Get|Hide|Join|Lock|Move|New|Open|Optimize|Pop|Push|Redo|Remove|Rename|Reset|Resize|Search|Select|Set|Show|Skip|Split|Step|Switch|Undo|Unlock|Watch|Backup|Checkpoint|Compare|Compress|Convert|ConvertFrom|ConvertTo|Dismount|Edit|Expand|Export|Group|Import|Initialize|Limit|Merge|Mount|Out|Publish|Restore|Save|Sync|Unpublish|Update|Approve|Assert|Build|Complete|Confirm|Deny|Deploy|Disable|Enable|Install|Invoke|Register|Request|Restart|Resume|Start|Stop|Submit|Suspend|Uninstall|Unregister|Wait|Debug|Measure|Ping|Repair|Resolve|Test|Trace|Connect|Disconnect|Read|Receive|Send|Write|Block|Grant|Protect|Revoke|Unblock|Unprotect|Use|ForEach|Sort|Tee|Where",")+(-)[\\w\\d]+")}]},C={className:"class",beginKeywords:"class enum",end:/\s*[{]/,excludeEnd:!0,relevance:0,contains:[s.TITLE_MODE]},j={className:"function",begin:/function\s+/,end:/\s*\{|$/,excludeEnd:!0,returnBegin:!0,relevance:0,contains:[{begin:"function",relevance:0,className:"keyword"},{className:"title",begin:/\w[\w\d]*((-)[\w\d]+)*/,relevance:0},{begin:/\(/,end:/\)/,className:"params",relevance:0,contains:[a]}]},L={begin:/using\s/,end:/$/,returnBegin:!0,contains:[u,_,{className:"keyword",begin:/(using|assembly|command|module|namespace|type)/}]},B={variants:[{className:"operator",begin:"(".concat("-and|-as|-band|-bnot|-bor|-bxor|-casesensitive|-ccontains|-ceq|-cge|-cgt|-cle|-clike|-clt|-cmatch|-cne|-cnotcontains|-cnotlike|-cnotmatch|-contains|-creplace|-csplit|-eq|-exact|-f|-file|-ge|-gt|-icontains|-ieq|-ige|-igt|-ile|-ilike|-ilt|-imatch|-in|-ine|-inotcontains|-inotlike|-inotmatch|-ireplace|-is|-isnot|-isplit|-join|-le|-like|-lt|-match|-ne|-not|-notcontains|-notin|-notlike|-notmatch|-or|-regex|-replace|-shl|-shr|-split|-wildcard|-xor",")\\b")},{className:"literal",begin:/(-)[\w\d]+/,relevance:0}]},$={className:"function",begin:/\[.*\]\s*[\w]+[ ]??\(/,end:/$/,returnBegin:!0,relevance:0,contains:[{className:"keyword",begin:"(".concat(o.keyword.toString().replace(/\s/g,"|"),")\\b"),endsParent:!0,relevance:0},s.inherit(s.TITLE_MODE,{endsParent:!0})]},z=[$,w,i,s.NUMBER_MODE,u,_,x,a,{className:"literal",begin:/\$(null|true|false)\b/},{className:"selector-tag",begin:/@\B/,relevance:0}],U={begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[].concat("self",z,{begin:"("+["string","char","byte","int","long","bool","decimal","single","double","DateTime","xml","array","hashtable","void"].join("|")+")",className:"built_in",relevance:0},{className:"type",begin:/[\.\w\d]+/,relevance:0})};return $.contains.unshift(U),{name:"PowerShell",aliases:["ps","ps1"],case_insensitive:!0,keywords:o,contains:z.concat(C,j,L,B,U)}}},17285(s){function source(s){return s?"string"==typeof s?s:s.source:null}function lookahead(s){return concat("(?=",s,")")}function concat(...s){return s.map((s=>source(s))).join("")}function either(...s){return"("+s.map((s=>source(s))).join("|")+")"}s.exports=function xml(s){const o=concat(/[A-Z_]/,function optional(s){return concat("(",s,")?")}(/[A-Z0-9_.-]*:/),/[A-Z0-9_.-]*/),i={className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},a={begin:/\s/,contains:[{className:"meta-keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]},u=s.inherit(a,{begin:/\(/,end:/\)/}),_=s.inherit(s.APOS_STRING_MODE,{className:"meta-string"}),w=s.inherit(s.QUOTE_STRING_MODE,{className:"meta-string"}),x={endsWithParent:!0,illegal:/`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin://,relevance:10,contains:[a,w,_,u,{begin:/\[/,end:/\]/,contains:[{className:"meta",begin://,contains:[a,u,w,_]}]}]},s.COMMENT(//,{relevance:10}),{begin://,relevance:10},i,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:/)/,end:/>/,keywords:{name:"style"},contains:[x],starts:{end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:/)/,end:/>/,keywords:{name:"script"},contains:[x],starts:{end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:/<>|<\/>/},{className:"tag",begin:concat(//,/>/,/\s/)))),end:/\/?>/,contains:[{className:"name",begin:o,relevance:0,starts:x}]},{className:"tag",begin:concat(/<\//,lookahead(concat(o,/>/))),contains:[{className:"name",begin:o,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}}},17533(s){s.exports=function yaml(s){var o="true false yes no null",i="[\\w#;/?:@&=+$,.~*'()[\\]]+",a={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[s.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},u=s.inherit(a,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),_={className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},w={end:",",endsWithParent:!0,excludeEnd:!0,keywords:o,relevance:0},x={begin:/\{/,end:/\}/,contains:[w],illegal:"\\n",relevance:0},C={begin:"\\[",end:"\\]",contains:[w],illegal:"\\n",relevance:0},j=[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$",relevance:10},{className:"string",begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+i},{className:"type",begin:"!<"+i+">"},{className:"type",begin:"!"+i},{className:"type",begin:"!!"+i},{className:"meta",begin:"&"+s.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+s.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)",relevance:0},s.HASH_COMMENT_MODE,{beginKeywords:o,keywords:{literal:o}},_,{className:"number",begin:s.C_NUMBER_RE+"\\b",relevance:0},x,C,a],L=[...j];return L.pop(),L.push(u),w.contains=L,{name:"YAML",case_insensitive:!0,aliases:["yml"],contains:j}}},251(s,o){o.read=function(s,o,i,a,u){var _,w,x=8*u-a-1,C=(1<>1,L=-7,B=i?u-1:0,$=i?-1:1,z=s[o+B];for(B+=$,_=z&(1<<-L)-1,z>>=-L,L+=x;L>0;_=256*_+s[o+B],B+=$,L-=8);for(w=_&(1<<-L)-1,_>>=-L,L+=a;L>0;w=256*w+s[o+B],B+=$,L-=8);if(0===_)_=1-j;else{if(_===C)return w?NaN:1/0*(z?-1:1);w+=Math.pow(2,a),_-=j}return(z?-1:1)*w*Math.pow(2,_-a)},o.write=function(s,o,i,a,u,_){var w,x,C,j=8*_-u-1,L=(1<>1,$=23===u?Math.pow(2,-24)-Math.pow(2,-77):0,z=a?0:_-1,U=a?1:-1,V=o<0||0===o&&1/o<0?1:0;for(o=Math.abs(o),isNaN(o)||o===1/0?(x=isNaN(o)?1:0,w=L):(w=Math.floor(Math.log(o)/Math.LN2),o*(C=Math.pow(2,-w))<1&&(w--,C*=2),(o+=w+B>=1?$/C:$*Math.pow(2,1-B))*C>=2&&(w++,C/=2),w+B>=L?(x=0,w=L):w+B>=1?(x=(o*C-1)*Math.pow(2,u),w+=B):(x=o*Math.pow(2,B-1)*Math.pow(2,u),w=0));u>=8;s[i+z]=255&x,z+=U,x/=256,u-=8);for(w=w<0;s[i+z]=255&w,z+=U,w/=256,j-=8);s[i+z-U]|=128*V}},49568(s,o,i){"use strict";i.r(o),i.d(o,{Collection:()=>w,Iterable:()=>Zt,List:()=>wt,Map:()=>ct,OrderedMap:()=>jt,OrderedSet:()=>Vt,PairSorting:()=>Ht,Range:()=>Rt,Record:()=>Kt,Repeat:()=>Xt,Seq:()=>le,Set:()=>Ft,Stack:()=>Tt,fromJS:()=>fromJS,get:()=>get,getIn:()=>getIn$1,has:()=>has,hasIn:()=>hasIn$1,hash:()=>hash,is:()=>is,isAssociative:()=>isAssociative,isCollection:()=>isCollection,isImmutable:()=>isImmutable,isIndexed:()=>isIndexed,isKeyed:()=>isKeyed,isList:()=>isList,isMap:()=>isMap,isOrdered:()=>isOrdered,isOrderedMap:()=>isOrderedMap,isOrderedSet:()=>isOrderedSet,isPlainObject:()=>isPlainObject,isRecord:()=>isRecord,isSeq:()=>isSeq,isSet:()=>isSet,isStack:()=>isStack,isValueObject:()=>isValueObject,merge:()=>merge,mergeDeep:()=>mergeDeep$1,mergeDeepWith:()=>mergeDeepWith$1,mergeWith:()=>mergeWith,remove:()=>remove,removeIn:()=>removeIn,set:()=>set,setIn:()=>setIn$1,update:()=>update$1,updateIn:()=>updateIn,version:()=>Qt});var a="@@__IMMUTABLE_INDEXED__@@";function isIndexed(s){return Boolean(s&&s[a])}var u="@@__IMMUTABLE_KEYED__@@";function isKeyed(s){return Boolean(s&&s[u])}function isAssociative(s){return isKeyed(s)||isIndexed(s)}var _="@@__IMMUTABLE_ITERABLE__@@";function isCollection(s){return Boolean(s&&s[_])}var w=function Collection(s){return isCollection(s)?s:le(s)},x=function(s){function KeyedCollection(s){return isKeyed(s)?s:pe(s)}return s&&(KeyedCollection.__proto__=s),KeyedCollection.prototype=Object.create(s&&s.prototype),KeyedCollection.prototype.constructor=KeyedCollection,KeyedCollection}(w),C=function(s){function IndexedCollection(s){return isIndexed(s)?s:de(s)}return s&&(IndexedCollection.__proto__=s),IndexedCollection.prototype=Object.create(s&&s.prototype),IndexedCollection.prototype.constructor=IndexedCollection,IndexedCollection}(w),j=function(s){function SetCollection(s){return isCollection(s)&&!isAssociative(s)?s:fe(s)}return s&&(SetCollection.__proto__=s),SetCollection.prototype=Object.create(s&&s.prototype),SetCollection.prototype.constructor=SetCollection,SetCollection}(w);w.Keyed=x,w.Indexed=C,w.Set=j;var L="function"==typeof Symbol&&Symbol.iterator,B="@@iterator",$=L||B,z=function Iterator(s){this.next=s};function iteratorValue(s,o,i,a){var u=0===s?o:1===s?i:[o,i];return a?a.value=u:a={value:u,done:!1},a}function iteratorDone(){return{value:void 0,done:!0}}function hasIterator(s){return!!Array.isArray(s)||!!getIteratorFn(s)}function isIterator(s){return!(!s||"function"!=typeof s.next)}function getIterator(s){var o=getIteratorFn(s);return o&&o.call(s)}function getIteratorFn(s){var o=s&&(L&&s[L]||s[B]);if("function"==typeof o)return o}z.prototype.toString=function toString(){return"[Iterator]"},z.KEYS=0,z.VALUES=1,z.ENTRIES=2,z.prototype.inspect=z.prototype.toSource=function(){return this.toString()},z.prototype[$]=function(){return this};var U="delete",V=32,Y=31,Z={};function SetRef(s){s&&(s.value=!0)}function OwnerID(){}function ensureSize(s){return void 0===s.size&&(s.size=s.__iterate(returnTrue)),s.size}function wrapIndex(s,o){if("number"!=typeof o){var i=o>>>0;if(""+i!==o||4294967295===i)return NaN;o=i}return o<0?ensureSize(s)+o:o}function returnTrue(){return!0}function wholeSlice(s,o,i){return(0===s&&!isNeg(s)||void 0!==i&&s<=-i)&&(void 0===o||void 0!==i&&o>=i)}function resolveBegin(s,o){return resolveIndex(s,o,0)}function resolveEnd(s,o){return resolveIndex(s,o,o)}function resolveIndex(s,o,i){return void 0===s?i:isNeg(s)?o===1/0?o:0|Math.max(0,o+s):void 0===o||o===s?s:0|Math.min(o,s)}function isNeg(s){return s<0||0===s&&1/s==-1/0}var ee="@@__IMMUTABLE_RECORD__@@";function isRecord(s){return Boolean(s&&s[ee])}function isImmutable(s){return isCollection(s)||isRecord(s)}var ie="@@__IMMUTABLE_ORDERED__@@";function isOrdered(s){return Boolean(s&&s[ie])}var ae="@@__IMMUTABLE_SEQ__@@";function isSeq(s){return Boolean(s&&s[ae])}var ce=Object.prototype.hasOwnProperty;function isArrayLike(s){return!(!Array.isArray(s)&&"string"!=typeof s)||s&&"object"==typeof s&&Number.isInteger(s.length)&&s.length>=0&&(0===s.length?1===Object.keys(s).length:s.hasOwnProperty(s.length-1))}var le=function(s){function Seq(s){return null==s?emptySequence():isImmutable(s)?s.toSeq():function seqFromValue(s){var o=maybeIndexedSeqFromValue(s);if(o)return function isEntriesIterable(s){var o=getIteratorFn(s);return o&&o===s.entries}(s)?o.fromEntrySeq():function isKeysIterable(s){var o=getIteratorFn(s);return o&&o===s.keys}(s)?o.toSetSeq():o;if("object"==typeof s)return new be(s);throw new TypeError("Expected Array or collection object of values, or keyed object: "+s)}(s)}return s&&(Seq.__proto__=s),Seq.prototype=Object.create(s&&s.prototype),Seq.prototype.constructor=Seq,Seq.prototype.toSeq=function toSeq(){return this},Seq.prototype.toString=function toString(){return this.__toString("Seq {","}")},Seq.prototype.cacheResult=function cacheResult(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},Seq.prototype.__iterate=function __iterate(s,o){var i=this._cache;if(i){for(var a=i.length,u=0;u!==a;){var _=i[o?a-++u:u++];if(!1===s(_[1],_[0],this))break}return u}return this.__iterateUncached(s,o)},Seq.prototype.__iterator=function __iterator(s,o){var i=this._cache;if(i){var a=i.length,u=0;return new z((function(){if(u===a)return{value:void 0,done:!0};var _=i[o?a-++u:u++];return iteratorValue(s,_[0],_[1])}))}return this.__iteratorUncached(s,o)},Seq}(w),pe=function(s){function KeyedSeq(s){return null==s?emptySequence().toKeyedSeq():isCollection(s)?isKeyed(s)?s.toSeq():s.fromEntrySeq():isRecord(s)?s.toSeq():keyedSeqFromValue(s)}return s&&(KeyedSeq.__proto__=s),KeyedSeq.prototype=Object.create(s&&s.prototype),KeyedSeq.prototype.constructor=KeyedSeq,KeyedSeq.prototype.toKeyedSeq=function toKeyedSeq(){return this},KeyedSeq}(le),de=function(s){function IndexedSeq(s){return null==s?emptySequence():isCollection(s)?isKeyed(s)?s.entrySeq():s.toIndexedSeq():isRecord(s)?s.toSeq().entrySeq():indexedSeqFromValue(s)}return s&&(IndexedSeq.__proto__=s),IndexedSeq.prototype=Object.create(s&&s.prototype),IndexedSeq.prototype.constructor=IndexedSeq,IndexedSeq.of=function of(){return IndexedSeq(arguments)},IndexedSeq.prototype.toIndexedSeq=function toIndexedSeq(){return this},IndexedSeq.prototype.toString=function toString(){return this.__toString("Seq [","]")},IndexedSeq}(le),fe=function(s){function SetSeq(s){return(isCollection(s)&&!isAssociative(s)?s:de(s)).toSetSeq()}return s&&(SetSeq.__proto__=s),SetSeq.prototype=Object.create(s&&s.prototype),SetSeq.prototype.constructor=SetSeq,SetSeq.of=function of(){return SetSeq(arguments)},SetSeq.prototype.toSetSeq=function toSetSeq(){return this},SetSeq}(le);le.isSeq=isSeq,le.Keyed=pe,le.Set=fe,le.Indexed=de,le.prototype[ae]=!0;var ye=function(s){function ArraySeq(s){this._array=s,this.size=s.length}return s&&(ArraySeq.__proto__=s),ArraySeq.prototype=Object.create(s&&s.prototype),ArraySeq.prototype.constructor=ArraySeq,ArraySeq.prototype.get=function get(s,o){return this.has(s)?this._array[wrapIndex(this,s)]:o},ArraySeq.prototype.__iterate=function __iterate(s,o){for(var i=this._array,a=i.length,u=0;u!==a;){var _=o?a-++u:u++;if(!1===s(i[_],_,this))break}return u},ArraySeq.prototype.__iterator=function __iterator(s,o){var i=this._array,a=i.length,u=0;return new z((function(){if(u===a)return{value:void 0,done:!0};var _=o?a-++u:u++;return iteratorValue(s,_,i[_])}))},ArraySeq}(de),be=function(s){function ObjectSeq(s){var o=Object.keys(s).concat(Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(s):[]);this._object=s,this._keys=o,this.size=o.length}return s&&(ObjectSeq.__proto__=s),ObjectSeq.prototype=Object.create(s&&s.prototype),ObjectSeq.prototype.constructor=ObjectSeq,ObjectSeq.prototype.get=function get(s,o){return void 0===o||this.has(s)?this._object[s]:o},ObjectSeq.prototype.has=function has(s){return ce.call(this._object,s)},ObjectSeq.prototype.__iterate=function __iterate(s,o){for(var i=this._object,a=this._keys,u=a.length,_=0;_!==u;){var w=a[o?u-++_:_++];if(!1===s(i[w],w,this))break}return _},ObjectSeq.prototype.__iterator=function __iterator(s,o){var i=this._object,a=this._keys,u=a.length,_=0;return new z((function(){if(_===u)return{value:void 0,done:!0};var w=a[o?u-++_:_++];return iteratorValue(s,w,i[w])}))},ObjectSeq}(pe);be.prototype[ie]=!0;var Se,_e=function(s){function CollectionSeq(s){this._collection=s,this.size=s.length||s.size}return s&&(CollectionSeq.__proto__=s),CollectionSeq.prototype=Object.create(s&&s.prototype),CollectionSeq.prototype.constructor=CollectionSeq,CollectionSeq.prototype.__iterateUncached=function __iterateUncached(s,o){if(o)return this.cacheResult().__iterate(s,o);var i=getIterator(this._collection),a=0;if(isIterator(i))for(var u;!(u=i.next()).done&&!1!==s(u.value,a++,this););return a},CollectionSeq.prototype.__iteratorUncached=function __iteratorUncached(s,o){if(o)return this.cacheResult().__iterator(s,o);var i=getIterator(this._collection);if(!isIterator(i))return new z(iteratorDone);var a=0;return new z((function(){var o=i.next();return o.done?o:iteratorValue(s,a++,o.value)}))},CollectionSeq}(de);function emptySequence(){return Se||(Se=new ye([]))}function keyedSeqFromValue(s){var o=maybeIndexedSeqFromValue(s);if(o)return o.fromEntrySeq();if("object"==typeof s)return new be(s);throw new TypeError("Expected Array or collection object of [k, v] entries, or keyed object: "+s)}function indexedSeqFromValue(s){var o=maybeIndexedSeqFromValue(s);if(o)return o;throw new TypeError("Expected Array or collection object of values: "+s)}function maybeIndexedSeqFromValue(s){return isArrayLike(s)?new ye(s):hasIterator(s)?new _e(s):void 0}function asImmutable(){return this.__ensureOwner()}function asMutable(){return this.__ownerID?this:this.__ensureOwner(new OwnerID)}var we="function"==typeof Math.imul&&-2===Math.imul(4294967295,2)?Math.imul:function imul(s,o){var i=65535&(s|=0),a=65535&(o|=0);return i*a+((s>>>16)*a+i*(o>>>16)<<16>>>0)|0};function smi(s){return s>>>1&1073741824|3221225471&s}var xe=Object.prototype.valueOf;function hash(s){if(null==s)return hashNullish(s);if("function"==typeof s.hashCode)return smi(s.hashCode(s));var o=function valueOf(s){return s.valueOf!==xe&&"function"==typeof s.valueOf?s.valueOf(s):s}(s);if(null==o)return hashNullish(o);switch(typeof o){case"boolean":return o?1108378657:1108378656;case"number":return function hashNumber(s){if(s!=s||s===1/0)return 0;var o=0|s;o!==s&&(o^=4294967295*s);for(;s>4294967295;)o^=s/=4294967295;return smi(o)}(o);case"string":return o.length>Ye?function cachedHashString(s){var o=et[s];void 0===o&&(o=hashString(s),Qe===Xe&&(Qe=0,et={}),Qe++,et[s]=o);return o}(o):hashString(o);case"object":case"function":return function hashJSObj(s){var o;if($e&&void 0!==(o=qe.get(s)))return o;if(void 0!==(o=s[He]))return o;if(!Re){if(void 0!==(o=s.propertyIsEnumerable&&s.propertyIsEnumerable[He]))return o;if(void 0!==(o=function getIENodeHash(s){if(s&&s.nodeType>0)switch(s.nodeType){case 1:return s.uniqueID;case 9:return s.documentElement&&s.documentElement.uniqueID}}(s)))return o}if(o=nextHash(),$e)qe.set(s,o);else{if(void 0!==Te&&!1===Te(s))throw new Error("Non-extensible objects are not allowed as keys.");if(Re)Object.defineProperty(s,He,{enumerable:!1,configurable:!1,writable:!1,value:o});else if(void 0!==s.propertyIsEnumerable&&s.propertyIsEnumerable===s.constructor.prototype.propertyIsEnumerable)s.propertyIsEnumerable=function(){return this.constructor.prototype.propertyIsEnumerable.apply(this,arguments)},s.propertyIsEnumerable[He]=o;else{if(void 0===s.nodeType)throw new Error("Unable to set a non-enumerable property on object.");s[He]=o}}return o}(o);case"symbol":return function hashSymbol(s){var o=ze[s];if(void 0!==o)return o;return o=nextHash(),ze[s]=o,o}(o);default:if("function"==typeof o.toString)return hashString(o.toString());throw new Error("Value type "+typeof o+" cannot be hashed.")}}function hashNullish(s){return null===s?1108378658:1108378659}function hashString(s){for(var o=0,i=0;i=0&&(C.get=function(o,i){return(o=wrapIndex(this,o))>=0&&o<_?s.get(o+w,i):i}),C.__iterateUncached=function(o,i){var u=this;if(0===_)return 0;if(i)return this.cacheResult().__iterate(o,i);var x=0,C=!0,j=0;return s.__iterate((function(s,i){if(!C||!(C=x++_)return{value:void 0,done:!0};var s=u.next();return a||1===o||s.done?s:iteratorValue(o,C-1,0===o?void 0:s.value[1],s)}))},C}function skipWhileFactory(s,o,i,a){var u=makeSequence(s);return u.__iterateUncached=function(u,_){var w=this;if(_)return this.cacheResult().__iterate(u,_);var x=!0,C=0;return s.__iterate((function(s,_,j){if(!x||!(x=o.call(i,s,_,j)))return C++,u(s,a?_:C-1,w)})),C},u.__iteratorUncached=function(u,_){var w=this;if(_)return this.cacheResult().__iterator(u,_);var x=s.__iterator(2,_),C=!0,j=0;return new z((function(){var s,_,L;do{if((s=x.next()).done)return a||1===u?s:iteratorValue(u,j++,0===u?void 0:s.value[1],s);var B=s.value;_=B[0],L=B[1],C&&(C=o.call(i,L,_,w))}while(C);return 2===u?s:iteratorValue(u,_,L,s)}))},u}rt.prototype.cacheResult=tt.prototype.cacheResult=nt.prototype.cacheResult=st.prototype.cacheResult=cacheResultThrough;var ot=function(s){function ConcatSeq(s){this._wrappedIterables=s.flatMap((function(s){return s._wrappedIterables?s._wrappedIterables:[s]})),this.size=this._wrappedIterables.reduce((function(s,o){if(void 0!==s){var i=o.size;if(void 0!==i)return s+i}}),0),this[u]=this._wrappedIterables[0][u],this[a]=this._wrappedIterables[0][a],this[ie]=this._wrappedIterables[0][ie]}return s&&(ConcatSeq.__proto__=s),ConcatSeq.prototype=Object.create(s&&s.prototype),ConcatSeq.prototype.constructor=ConcatSeq,ConcatSeq.prototype.__iterateUncached=function __iterateUncached(s,o){if(0!==this._wrappedIterables.length){if(o)return this.cacheResult().__iterate(s,o);for(var i=0,a=isKeyed(this),u=a?2:1,_=this._wrappedIterables[i].__iterator(u,o),w=!0,x=0;w;){for(var C=_.next();C.done;){if(++i===this._wrappedIterables.length)return x;C=(_=this._wrappedIterables[i].__iterator(u,o)).next()}w=!1!==(a?s(C.value[1],C.value[0],this):s(C.value,x,this)),x++}return x}},ConcatSeq.prototype.__iteratorUncached=function __iteratorUncached(s,o){var i=this;if(0===this._wrappedIterables.length)return new z(iteratorDone);if(o)return this.cacheResult().__iterator(s,o);var a=0,u=this._wrappedIterables[a].__iterator(s,o);return new z((function(){for(var _=u.next();_.done;){if(++a===i._wrappedIterables.length)return _;_=(u=i._wrappedIterables[a].__iterator(s,o)).next()}return _}))},ConcatSeq}(le);function flattenFactory(s,o,i){var a=makeSequence(s);return a.__iterateUncached=function(u,_){if(_)return this.cacheResult().__iterate(u,_);var w=0,x=!1;return function flatDeep(s,C){s.__iterate((function(s,_){return(!o||C0}function zipWithFactory(s,o,i,a){var u=makeSequence(s),_=new ye(i).map((function(s){return s.size}));return u.size=a?_.max():_.min(),u.__iterate=function(s,o){for(var i,a=this.__iterator(1,o),u=0;!(i=a.next()).done&&!1!==s(i.value,u++,this););return u},u.__iteratorUncached=function(s,u){var _=i.map((function(s){return s=w(s),getIterator(u?s.reverse():s)})),x=0,C=!1;return new z((function(){var i;return C||(i=_.map((function(s){return s.next()})),C=a?i.every((function(s){return s.done})):i.some((function(s){return s.done}))),C?{value:void 0,done:!0}:iteratorValue(s,x++,o.apply(null,i.map((function(s){return s.value}))))}))},u}function reify(s,o){return s===o?s:isSeq(s)?o:s.constructor(o)}function validateEntry(s){if(s!==Object(s))throw new TypeError("Expected [K, V] tuple: "+s)}function collectionClass(s){return isKeyed(s)?x:isIndexed(s)?C:j}function makeSequence(s){return Object.create((isKeyed(s)?pe:isIndexed(s)?de:fe).prototype)}function cacheResultThrough(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):le.prototype.cacheResult.call(this)}function defaultComparator(s,o){return void 0===s&&void 0===o?0:void 0===s?1:void 0===o?-1:s>o?1:s0;)o[i]=arguments[i+1];if("function"!=typeof s)throw new TypeError("Invalid merger function: "+s);return mergeIntoKeyedWith(this,o,s)}function mergeIntoKeyedWith(s,o,i){for(var a=[],u=0;u0;)o[i]=arguments[i+1];return mergeWithSources(s,o)}function mergeWith(s,o){for(var i=[],a=arguments.length-2;a-- >0;)i[a]=arguments[a+2];return mergeWithSources(o,i,s)}function mergeDeep$1(s){for(var o=[],i=arguments.length-1;i-- >0;)o[i]=arguments[i+1];return mergeDeepWithSources(s,o)}function mergeDeepWith$1(s,o){for(var i=[],a=arguments.length-2;a-- >0;)i[a]=arguments[a+2];return mergeDeepWithSources(o,i,s)}function mergeDeepWithSources(s,o,i){return mergeWithSources(s,o,function deepMergerWith(s){function deepMerger(o,i,a){return isDataStructure(o)&&isDataStructure(i)&&function areMergeable(s,o){var i=le(s),a=le(o);return isIndexed(i)===isIndexed(a)&&isKeyed(i)===isKeyed(a)}(o,i)?mergeWithSources(o,[i],deepMerger):s?s(o,i,a):i}return deepMerger}(i))}function mergeWithSources(s,o,i){if(!isDataStructure(s))throw new TypeError("Cannot merge into non-data-structure value: "+s);if(isImmutable(s))return"function"==typeof i&&s.mergeWith?s.mergeWith.apply(s,[i].concat(o)):s.merge?s.merge.apply(s,o):s.concat.apply(s,o);for(var a=Array.isArray(s),u=s,_=a?C:x,w=a?function(o){u===s&&(u=shallowCopy(u)),u.push(o)}:function(o,a){if(!isProtoKey(a)){var _=ce.call(u,a),w=_&&i?i(u[a],o,a):o;_&&w===u[a]||(u===s&&(u=shallowCopy(u)),u[a]=w)}},j=0;j0;)o[i]=arguments[i+1];return mergeDeepWithSources(this,o,s)}function mergeDeepIn(s){for(var o=[],i=arguments.length-1;i-- >0;)o[i]=arguments[i+1];return updateIn(this,s,emptyMap(),(function(s){return mergeDeepWithSources(s,o)}))}function mergeIn(s){for(var o=[],i=arguments.length-1;i-- >0;)o[i]=arguments[i+1];return updateIn(this,s,emptyMap(),(function(s){return mergeWithSources(s,o)}))}function setIn$1(s,o,i){return updateIn(s,o,Z,(function(){return i}))}function setIn(s,o){return setIn$1(this,s,o)}function update(s,o,i){return 1===arguments.length?s(this):update$1(this,s,o,i)}function updateIn$1(s,o,i){return updateIn(this,s,o,i)}function wasAltered(){return this.__altered}function withMutations(s){var o=this.asMutable();return s(o),o.wasAltered()?o.__ensureOwner(this.__ownerID):this}var at="@@__IMMUTABLE_MAP__@@";function isMap(s){return Boolean(s&&s[at])}function invariant(s,o){if(!s)throw new Error(o)}function assertNotInfinite(s){invariant(s!==1/0,"Cannot perform this action with an infinite size.")}var ct=function(s){function Map(o){return null==o?emptyMap():isMap(o)&&!isOrdered(o)?o:emptyMap().withMutations((function(i){var a=s(o);assertNotInfinite(a.size),a.forEach((function(s,o){return i.set(o,s)}))}))}return s&&(Map.__proto__=s),Map.prototype=Object.create(s&&s.prototype),Map.prototype.constructor=Map,Map.prototype.toString=function toString(){return this.__toString("Map {","}")},Map.prototype.get=function get(s,o){return this._root?this._root.get(0,void 0,s,o):o},Map.prototype.set=function set(s,o){return updateMap(this,s,o)},Map.prototype.remove=function remove(s){return updateMap(this,s,Z)},Map.prototype.deleteAll=function deleteAll(s){var o=w(s);return 0===o.size?this:this.withMutations((function(s){o.forEach((function(o){return s.remove(o)}))}))},Map.prototype.clear=function clear(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):emptyMap()},Map.prototype.sort=function sort(s){return jt(sortFactory(this,s))},Map.prototype.sortBy=function sortBy(s,o){return jt(sortFactory(this,o,s))},Map.prototype.map=function map(s,o){var i=this;return this.withMutations((function(a){a.forEach((function(u,_){a.set(_,s.call(o,u,_,i))}))}))},Map.prototype.__iterator=function __iterator(s,o){return new yt(this,s,o)},Map.prototype.__iterate=function __iterate(s,o){var i=this,a=0;return this._root&&this._root.iterate((function(o){return a++,s(o[1],o[0],i)}),o),a},Map.prototype.__ensureOwner=function __ensureOwner(s){return s===this.__ownerID?this:s?makeMap(this.size,this._root,s,this.__hash):0===this.size?emptyMap():(this.__ownerID=s,this.__altered=!1,this)},Map}(x);ct.isMap=isMap;var lt=ct.prototype;lt[at]=!0,lt[U]=lt.remove,lt.removeAll=lt.deleteAll,lt.setIn=setIn,lt.removeIn=lt.deleteIn=deleteIn,lt.update=update,lt.updateIn=updateIn$1,lt.merge=lt.concat=merge$1,lt.mergeWith=mergeWith$1,lt.mergeDeep=mergeDeep,lt.mergeDeepWith=mergeDeepWith,lt.mergeIn=mergeIn,lt.mergeDeepIn=mergeDeepIn,lt.withMutations=withMutations,lt.wasAltered=wasAltered,lt.asImmutable=asImmutable,lt["@@transducer/init"]=lt.asMutable=asMutable,lt["@@transducer/step"]=function(s,o){return s.set(o[0],o[1])},lt["@@transducer/result"]=function(s){return s.asImmutable()};var ut=function ArrayMapNode(s,o){this.ownerID=s,this.entries=o};ut.prototype.get=function get(s,o,i,a){for(var u=this.entries,_=0,w=u.length;_=vt)return function createNodes(s,o,i,a){s||(s=new OwnerID);for(var u=new mt(s,hash(i),[i,a]),_=0;_>>s)&Y),_=this.bitmap;return _&u?this.nodes[popCount(_&u-1)].get(s+5,o,i,a):a},pt.prototype.update=function update(s,o,i,a,u,_,w){void 0===i&&(i=hash(a));var x=(0===o?i:i>>>o)&Y,C=1<=bt)return function expandNodes(s,o,i,a,u){for(var _=0,w=new Array(V),x=0;0!==i;x++,i>>>=1)w[x]=1&i?o[_++]:void 0;return w[a]=u,new ht(s,_+1,w)}(s,$,j,x,U);if(L&&!U&&2===$.length&&isLeafNode($[1^B]))return $[1^B];if(L&&U&&1===$.length&&isLeafNode(U))return U;var ee=s&&s===this.ownerID,ie=L?U?j:j^C:j|C,ae=L?U?setAt($,B,U,ee):function spliceOut(s,o,i){var a=s.length-1;if(i&&o===a)return s.pop(),s;for(var u=new Array(a),_=0,w=0;w>>s)&Y,_=this.nodes[u];return _?_.get(s+5,o,i,a):a},ht.prototype.update=function update(s,o,i,a,u,_,w){void 0===i&&(i=hash(a));var x=(0===o?i:i>>>o)&Y,C=u===Z,j=this.nodes,L=j[x];if(C&&!L)return this;var B=updateNode(L,s,o+5,i,a,u,_,w);if(B===L)return this;var $=this.count;if(L){if(!B&&--$=_t&&(a=this._buildIndex()),void 0!==a){var u=a[hashCollisionKey(s)];if(void 0!==u)for(var _=0;_>>i)&Y,x=(0===i?a:a>>>i)&Y,C=w===x?[mergeIntoNode(s,o,i+5,a,u)]:(_=new mt(o,a,u),w>1&1431655765))+(s>>2&858993459))+(s>>4)&252645135,s+=s>>8,127&(s+=s>>16)}function setAt(s,o,i,a){var u=a?s:arrCopy(s);return u[o]=i,u}var vt=8,bt=16,St=8,_t=16;function coerceKeyPath(s){if(isArrayLike(s)&&"string"!=typeof s)return s;if(isOrdered(s))return s.toArray();throw new TypeError("Invalid keyPath: expected Ordered Collection or Array: "+s)}function quoteString(s){try{return"string"==typeof s?JSON.stringify(s):String(s)}catch(o){return JSON.stringify(s)}}function has(s,o){return isImmutable(s)?s.has(o):isDataStructure(s)&&ce.call(s,o)}function get(s,o,i){return isImmutable(s)?s.get(o,i):has(s,o)?"function"==typeof s.get?s.get(o):s[o]:i}function remove(s,o){if(!isDataStructure(s))throw new TypeError("Cannot update non-data-structure value: "+s);if(isImmutable(s)){if(!s.remove)throw new TypeError("Cannot update immutable value without .remove() method: "+s);return s.remove(o)}if(!ce.call(s,o))return s;var i=shallowCopy(s);return Array.isArray(i)?i.splice(o,1):delete i[o],i}function set(s,o,i){if(isProtoKey(o))return s;if(!isDataStructure(s))throw new TypeError("Cannot update non-data-structure value: "+s);if(isImmutable(s)){if(!s.set)throw new TypeError("Cannot update immutable value without .set() method: "+s);return s.set(o,i)}if(ce.call(s,o)&&i===s[o])return s;var a=shallowCopy(s);return a[o]=i,a}function updateIn(s,o,i,a){a||(a=i,i=void 0);var u=updateInDeeply(isImmutable(s),s,coerceKeyPath(o),0,i,a);return u===Z?i:u}function updateInDeeply(s,o,i,a,u,_){var w=o===Z;if(a===i.length){var x=w?u:o,C=_(x);return C===x?o:C}if(!w&&!isDataStructure(o))throw new TypeError("Cannot update within non-data-structure value in path ["+Array.from(i).slice(0,a).map(quoteString)+"]: "+o);var j=i[a],L=w?Z:get(o,j,Z),B=updateInDeeply(L===Z?s:isImmutable(L),L,i,a+1,u,_);return B===L?o:B===Z?remove(o,j):set(w?s?emptyMap():{}:o,j,B)}function removeIn(s,o){return updateIn(s,o,(function(){return Z}))}function deleteIn(s){return removeIn(this,s)}var Et="@@__IMMUTABLE_LIST__@@";function isList(s){return Boolean(s&&s[Et])}var wt=function(s){function List(o){var i=emptyList();if(null==o)return i;if(isList(o))return o;var a=s(o),u=a.size;return 0===u?i:(assertNotInfinite(u),u>0&&u=0&&s=s.size||o<0)return s.withMutations((function(s){o<0?setListBounds(s,o).set(0,i):setListBounds(s,0,o+1).set(o,i)}));o+=s._origin;var a=s._tail,u=s._root,_={value:!1};o>=getTailOffset(s._capacity)?a=updateVNode(a,s.__ownerID,0,o,i,_):u=updateVNode(u,s.__ownerID,s._level,o,i,_);if(!_.value)return s;if(s.__ownerID)return s._root=u,s._tail=a,s.__hash=void 0,s.__altered=!0,s;return makeList(s._origin,s._capacity,s._level,u,a)}(this,s,o)},List.prototype.remove=function remove(s){return this.has(s)?0===s?this.shift():s===this.size-1?this.pop():this.splice(s,1):this},List.prototype.insert=function insert(s,o){return this.splice(s,0,o)},List.prototype.clear=function clear(){return 0===this.size?this:this.__ownerID?(this.size=this._origin=this._capacity=0,this._level=5,this._root=this._tail=this.__hash=void 0,this.__altered=!0,this):emptyList()},List.prototype.push=function push(){var s=arguments,o=this.size;return this.withMutations((function(i){setListBounds(i,0,o+s.length);for(var a=0;a>>o&Y;if(a>=this.array.length)return new kt([],s);var u,_=0===a;if(o>0){var w=this.array[a];if((u=w&&w.removeBefore(s,o-5,i))===w&&_)return this}if(_&&!u)return this;var x=editableVNode(this,s);if(!_)for(var C=0;C>>o&Y;if(u>=this.array.length)return this;if(o>0){var _=this.array[u];if((a=_&&_.removeAfter(s,o-5,i))===_&&u===this.array.length-1)return this}var w=editableVNode(this,s);return w.array.splice(u+1),a&&(w.array[u]=a),w};var Ot={};function iterateList(s,o){var i=s._origin,a=s._capacity,u=getTailOffset(a),_=s._tail;return iterateNodeOrLeaf(s._root,s._level,0);function iterateNodeOrLeaf(s,w,x){return 0===w?function iterateLeaf(s,w){var x=w===u?_&&_.array:s&&s.array,C=w>i?0:i-w,j=a-w;j>V&&(j=V);return function(){if(C===j)return Ot;var s=o?--j:C++;return x&&x[s]}}(s,x):function iterateNode(s,u,_){var w,x=s&&s.array,C=_>i?0:i-_>>u,j=1+(a-_>>u);j>V&&(j=V);return function(){for(;;){if(w){var s=w();if(s!==Ot)return s;w=null}if(C===j)return Ot;var i=o?--j:C++;w=iterateNodeOrLeaf(x&&x[i],u-5,_+(i<>>i&Y,C=s&&x0){var j=s&&s.array[x],L=updateVNode(j,o,i-5,a,u,_);return L===j?s:((w=editableVNode(s,o)).array[x]=L,w)}return C&&s.array[x]===u?s:(_&&SetRef(_),w=editableVNode(s,o),void 0===u&&x===w.array.length-1?w.array.pop():w.array[x]=u,w)}function editableVNode(s,o){return o&&s&&o===s.ownerID?s:new kt(s?s.array.slice():[],o)}function listNodeFor(s,o){if(o>=getTailOffset(s._capacity))return s._tail;if(o<1<0;)i=i.array[o>>>a&Y],a-=5;return i}}function setListBounds(s,o,i){!function validateListBoundsRequest(s,o,i){var a=s._origin+(void 0===o?0:o),u=void 0===i?s._capacity:i<0?s._capacity+i:s._origin+i;if(Number.isFinite(u)&&u>At||Number.isFinite(a)&&a<-At||Number.isFinite(u)&&Number.isFinite(a)&&u-a>At)throw new RangeError("Invalid List size: a List cannot hold more than "+At+" (2 ** 30) values.")}(s,o,i),void 0!==o&&(o|=0),void 0!==i&&(i|=0);var a=s.__ownerID||new OwnerID,u=s._origin,_=s._capacity,w=u+o,x=void 0===i?_:i<0?_+i:u+i;if(w===u&&x===_)return s;if(w>=x)return s.clear();for(var C=s._level,j=s._root,L=0;w+L<0;)j=new kt(j&&j.array.length?[void 0,j]:[],a),L+=levelCapacity(C+=5);L&&(w+=L,u+=L,x+=L,_+=L);for(var B=getTailOffset(_),$=getTailOffset(x);$>=levelCapacity(C+5);)j=new kt(j&&j.array.length?[j]:[],a),C+=5;var z=s._tail,U=$B?new kt([],a):z;if(z&&$>B&&w<_&&z.array.length){for(var V=j=editableVNode(j,a),Z=C;Z>5;Z-=5){var ee=B>>>Z&Y;V=V.array[ee]=editableVNode(V.array[ee],a)}V.array[B>>>5&Y]=z}if(x<_&&(U=U&&U.removeAfter(a,0,x)),w>=$)w-=$,x-=$,C=5,j=void 0,U=U&&U.removeBefore(a,0,w);else if(w>u||$>>C&Y;if(ie!==$>>>C&Y)break;ie&&(L+=(1<u&&(j=j.removeBefore(a,C,w-L)),j&&$>>5<<5}var At=Math.pow(2,30);function levelCapacity(s){return s<31?1<=V&&w.size>=2*_.size?(a=(u=w.filter((function(s,o){return void 0!==s&&x!==o}))).toKeyedSeq().map((function(s){return s[0]})).flip().toMap(),s.__ownerID&&(a.__ownerID=u.__ownerID=s.__ownerID)):(a=_.remove(o),u=x===w.size-1?w.pop():w.set(x,void 0))}else if(C){if(i===w.get(x)[1])return s;a=_,u=w.set(x,[o,i])}else a=_.set(o,w.size),u=w.set(w.size,[o,i]);return s.__ownerID?(s.size=a.size,s._map=a,s._list=u,s.__hash=void 0,s.__altered=!0,s):makeOrderedMap(a,u)}jt.isOrderedMap=isOrderedMap,jt.prototype[ie]=!0,jt.prototype[U]=jt.prototype.remove;var Pt="@@__IMMUTABLE_STACK__@@";function isStack(s){return Boolean(s&&s[Pt])}var Tt=function(s){function Stack(s){return null==s?emptyStack():isStack(s)?s:emptyStack().pushAll(s)}return s&&(Stack.__proto__=s),Stack.prototype=Object.create(s&&s.prototype),Stack.prototype.constructor=Stack,Stack.of=function of(){return this(arguments)},Stack.prototype.toString=function toString(){return this.__toString("Stack [","]")},Stack.prototype.get=function get(s,o){var i=this._head;for(s=wrapIndex(this,s);i&&s--;)i=i.next;return i?i.value:o},Stack.prototype.peek=function peek(){return this._head&&this._head.value},Stack.prototype.push=function push(){var s=arguments;if(0===arguments.length)return this;for(var o=this.size+arguments.length,i=this._head,a=arguments.length-1;a>=0;a--)i={value:s[a],next:i};return this.__ownerID?(this.size=o,this._head=i,this.__hash=void 0,this.__altered=!0,this):makeStack(o,i)},Stack.prototype.pushAll=function pushAll(o){if(0===(o=s(o)).size)return this;if(0===this.size&&isStack(o))return o;assertNotInfinite(o.size);var i=this.size,a=this._head;return o.__iterate((function(s){i++,a={value:s,next:a}}),!0),this.__ownerID?(this.size=i,this._head=a,this.__hash=void 0,this.__altered=!0,this):makeStack(i,a)},Stack.prototype.pop=function pop(){return this.slice(1)},Stack.prototype.clear=function clear(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):emptyStack()},Stack.prototype.slice=function slice(o,i){if(wholeSlice(o,i,this.size))return this;var a=resolveBegin(o,this.size);if(resolveEnd(i,this.size)!==this.size)return s.prototype.slice.call(this,o,i);for(var u=this.size-a,_=this._head;a--;)_=_.next;return this.__ownerID?(this.size=u,this._head=_,this.__hash=void 0,this.__altered=!0,this):makeStack(u,_)},Stack.prototype.__ensureOwner=function __ensureOwner(s){return s===this.__ownerID?this:s?makeStack(this.size,this._head,s,this.__hash):0===this.size?emptyStack():(this.__ownerID=s,this.__altered=!1,this)},Stack.prototype.__iterate=function __iterate(s,o){var i=this;if(o)return new ye(this.toArray()).__iterate((function(o,a){return s(o,a,i)}),o);for(var a=0,u=this._head;u&&!1!==s(u.value,a++,this);)u=u.next;return a},Stack.prototype.__iterator=function __iterator(s,o){if(o)return new ye(this.toArray()).__iterator(s,o);var i=0,a=this._head;return new z((function(){if(a){var o=a.value;return a=a.next,iteratorValue(s,i++,o)}return{value:void 0,done:!0}}))},Stack}(C);Tt.isStack=isStack;var It,Nt=Tt.prototype;function makeStack(s,o,i,a){var u=Object.create(Nt);return u.size=s,u._head=o,u.__ownerID=i,u.__hash=a,u.__altered=!1,u}function emptyStack(){return It||(It=makeStack(0))}function reduce(s,o,i,a,u,_){return assertNotInfinite(s.size),s.__iterate((function(s,_,w){u?(u=!1,i=s):i=o.call(a,i,s,_,w)}),_),i}function keyMapper(s,o){return o}function entryMapper(s,o){return[o,s]}function not(s){return function(){for(var o=[],i=arguments.length;i--;)o[i]=arguments[i];return!s.apply(this,o)}}function neg(s){return function(){for(var o=[],i=arguments.length;i--;)o[i]=arguments[i];return-s.apply(this,o)}}function defaultNegComparator(s,o){return so?-1:0}function deepEqual(s,o){if(s===o)return!0;if(!isCollection(o)||void 0!==s.size&&void 0!==o.size&&s.size!==o.size||void 0!==s.__hash&&void 0!==o.__hash&&s.__hash!==o.__hash||isKeyed(s)!==isKeyed(o)||isIndexed(s)!==isIndexed(o)||isOrdered(s)!==isOrdered(o))return!1;if(0===s.size&&0===o.size)return!0;var i=!isAssociative(s);if(isOrdered(s)){var a=s.entries();return o.every((function(s,o){var u=a.next().value;return u&&is(u[1],s)&&(i||is(u[0],o))}))&&a.next().done}var u=!1;if(void 0===s.size)if(void 0===o.size)"function"==typeof s.cacheResult&&s.cacheResult();else{u=!0;var _=s;s=o,o=_}var w=!0,x=o.__iterate((function(o,a){if(i?!s.has(o):u?!is(o,s.get(a,Z)):!is(s.get(a,Z),o))return w=!1,!1}));return w&&s.size===x}Nt[Pt]=!0,Nt.shift=Nt.pop,Nt.unshift=Nt.push,Nt.unshiftAll=Nt.pushAll,Nt.withMutations=withMutations,Nt.wasAltered=wasAltered,Nt.asImmutable=asImmutable,Nt["@@transducer/init"]=Nt.asMutable=asMutable,Nt["@@transducer/step"]=function(s,o){return s.unshift(o)},Nt["@@transducer/result"]=function(s){return s.asImmutable()};var Mt,Rt=function(s){function Range(s,o,i){if(void 0===i&&(i=1),!(this instanceof Range))return new Range(s,o,i);if(invariant(0!==i,"Cannot step a Range by 0"),invariant(void 0!==s,"You must define a start value when using Range"),invariant(void 0!==o,"You must define an end value when using Range"),i=Math.abs(i),o=0&&o=0&&i>>-15,461845907),o=we(o<<13|o>>>-13,5),o=o+3864292196^s,o=we(o^o>>>16,2246822507),o=we(o^o>>>13,3266489909),o=smi(o^o>>>16),o}(s.size,a)}function hashMerge(s,o){return s^o+2654435769+(s<<6)+(s>>2)}function mixin(s,o){var keyCopier=function(i){s.prototype[i]=o[i]};return Object.keys(o).forEach(keyCopier),Object.getOwnPropertySymbols&&Object.getOwnPropertySymbols(o).forEach(keyCopier),s}Bt[Dt]=!0,Bt[U]=Bt.remove,Bt.merge=Bt.concat=Bt.union,Bt.withMutations=withMutations,Bt.asImmutable=asImmutable,Bt["@@transducer/init"]=Bt.asMutable=asMutable,Bt["@@transducer/step"]=function(s,o){return s.add(o)},Bt["@@transducer/result"]=function(s){return s.asImmutable()},Bt.__empty=emptySet,Bt.__make=makeSet,w.Iterator=z,mixin(w,{toArray:function toArray(){assertNotInfinite(this.size);var s=new Array(this.size||0),o=isKeyed(this),i=0;return this.__iterate((function(a,u){s[i++]=o?[u,a]:a})),s},toIndexedSeq:function toIndexedSeq(){return new rt(this)},toJS:function toJS$1(){return toJS(this)},toKeyedSeq:function toKeyedSeq(){return new tt(this,!0)},toMap:function toMap(){return ct(this.toKeyedSeq())},toObject,toOrderedMap:function toOrderedMap(){return jt(this.toKeyedSeq())},toOrderedSet:function toOrderedSet(){return Vt(isKeyed(this)?this.valueSeq():this)},toSet:function toSet(){return Ft(isKeyed(this)?this.valueSeq():this)},toSetSeq:function toSetSeq(){return new nt(this)},toSeq:function toSeq(){return isIndexed(this)?this.toIndexedSeq():isKeyed(this)?this.toKeyedSeq():this.toSetSeq()},toStack:function toStack(){return Tt(isKeyed(this)?this.valueSeq():this)},toList:function toList(){return wt(isKeyed(this)?this.valueSeq():this)},toString:function toString(){return"[Collection]"},__toString:function __toString(s,o){return 0===this.size?s+o:s+" "+this.toSeq().map(this.__toStringMapper).join(", ")+" "+o},concat:function concat(){for(var s=[],o=arguments.length;o--;)s[o]=arguments[o];return reify(this,function concatFactory(s,o){var i=isKeyed(s),a=[s].concat(o).map((function(s){return isCollection(s)?i&&(s=x(s)):s=i?keyedSeqFromValue(s):indexedSeqFromValue(Array.isArray(s)?s:[s]),s})).filter((function(s){return 0!==s.size}));if(0===a.length)return s;if(1===a.length){var u=a[0];if(u===s||i&&isKeyed(u)||isIndexed(s)&&isIndexed(u))return u}return new ot(a)}(this,s))},includes:function includes(s){return this.some((function(o){return is(o,s)}))},entries:function entries(){return this.__iterator(2)},every:function every(s,o){assertNotInfinite(this.size);var i=!0;return this.__iterate((function(a,u,_){if(!s.call(o,a,u,_))return i=!1,!1})),i},filter:function filter(s,o){return reify(this,filterFactory(this,s,o,!0))},partition:function partition(s,o){return function partitionFactory(s,o,i){var a=isKeyed(s),u=[[],[]];s.__iterate((function(_,w){u[o.call(i,_,w,s)?1:0].push(a?[w,_]:_)}));var _=collectionClass(s);return u.map((function(o){return reify(s,_(o))}))}(this,s,o)},find:function find(s,o,i){var a=this.findEntry(s,o);return a?a[1]:i},forEach:function forEach(s,o){return assertNotInfinite(this.size),this.__iterate(o?s.bind(o):s)},join:function join(s){assertNotInfinite(this.size),s=void 0!==s?""+s:",";var o="",i=!0;return this.__iterate((function(a){i?i=!1:o+=s,o+=null!=a?a.toString():""})),o},keys:function keys(){return this.__iterator(0)},map:function map(s,o){return reify(this,mapFactory(this,s,o))},reduce:function reduce$1(s,o,i){return reduce(this,s,o,i,arguments.length<2,!1)},reduceRight:function reduceRight(s,o,i){return reduce(this,s,o,i,arguments.length<2,!0)},reverse:function reverse(){return reify(this,reverseFactory(this,!0))},slice:function slice(s,o){return reify(this,sliceFactory(this,s,o,!0))},some:function some(s,o){assertNotInfinite(this.size);var i=!1;return this.__iterate((function(a,u,_){if(s.call(o,a,u,_))return i=!0,!1})),i},sort:function sort(s){return reify(this,sortFactory(this,s))},values:function values(){return this.__iterator(1)},butLast:function butLast(){return this.slice(0,-1)},isEmpty:function isEmpty(){return void 0!==this.size?0===this.size:!this.some((function(){return!0}))},count:function count(s,o){return ensureSize(s?this.toSeq().filter(s,o):this)},countBy:function countBy(s,o){return function countByFactory(s,o,i){var a=ct().asMutable();return s.__iterate((function(u,_){a.update(o.call(i,u,_,s),0,(function(s){return s+1}))})),a.asImmutable()}(this,s,o)},equals:function equals(s){return deepEqual(this,s)},entrySeq:function entrySeq(){var s=this;if(s._cache)return new ye(s._cache);var o=s.toSeq().map(entryMapper).toIndexedSeq();return o.fromEntrySeq=function(){return s.toSeq()},o},filterNot:function filterNot(s,o){return this.filter(not(s),o)},findEntry:function findEntry(s,o,i){var a=i;return this.__iterate((function(i,u,_){if(s.call(o,i,u,_))return a=[u,i],!1})),a},findKey:function findKey(s,o){var i=this.findEntry(s,o);return i&&i[0]},findLast:function findLast(s,o,i){return this.toKeyedSeq().reverse().find(s,o,i)},findLastEntry:function findLastEntry(s,o,i){return this.toKeyedSeq().reverse().findEntry(s,o,i)},findLastKey:function findLastKey(s,o){return this.toKeyedSeq().reverse().findKey(s,o)},first:function first(s){return this.find(returnTrue,null,s)},flatMap:function flatMap(s,o){return reify(this,function flatMapFactory(s,o,i){var a=collectionClass(s);return s.toSeq().map((function(u,_){return a(o.call(i,u,_,s))})).flatten(!0)}(this,s,o))},flatten:function flatten(s){return reify(this,flattenFactory(this,s,!0))},fromEntrySeq:function fromEntrySeq(){return new st(this)},get:function get(s,o){return this.find((function(o,i){return is(i,s)}),void 0,o)},getIn,groupBy:function groupBy(s,o){return function groupByFactory(s,o,i){var a=isKeyed(s),u=(isOrdered(s)?jt():ct()).asMutable();s.__iterate((function(_,w){u.update(o.call(i,_,w,s),(function(s){return(s=s||[]).push(a?[w,_]:_),s}))}));var _=collectionClass(s);return u.map((function(o){return reify(s,_(o))})).asImmutable()}(this,s,o)},has:function has(s){return this.get(s,Z)!==Z},hasIn:function hasIn(s){return hasIn$1(this,s)},isSubset:function isSubset(s){return s="function"==typeof s.includes?s:w(s),this.every((function(o){return s.includes(o)}))},isSuperset:function isSuperset(s){return(s="function"==typeof s.isSubset?s:w(s)).isSubset(this)},keyOf:function keyOf(s){return this.findKey((function(o){return is(o,s)}))},keySeq:function keySeq(){return this.toSeq().map(keyMapper).toIndexedSeq()},last:function last(s){return this.toSeq().reverse().first(s)},lastKeyOf:function lastKeyOf(s){return this.toKeyedSeq().reverse().keyOf(s)},max:function max(s){return maxFactory(this,s)},maxBy:function maxBy(s,o){return maxFactory(this,o,s)},min:function min(s){return maxFactory(this,s?neg(s):defaultNegComparator)},minBy:function minBy(s,o){return maxFactory(this,o?neg(o):defaultNegComparator,s)},rest:function rest(){return this.slice(1)},skip:function skip(s){return 0===s?this:this.slice(Math.max(0,s))},skipLast:function skipLast(s){return 0===s?this:this.slice(0,-Math.max(0,s))},skipWhile:function skipWhile(s,o){return reify(this,skipWhileFactory(this,s,o,!0))},skipUntil:function skipUntil(s,o){return this.skipWhile(not(s),o)},sortBy:function sortBy(s,o){return reify(this,sortFactory(this,o,s))},take:function take(s){return this.slice(0,Math.max(0,s))},takeLast:function takeLast(s){return this.slice(-Math.max(0,s))},takeWhile:function takeWhile(s,o){return reify(this,function takeWhileFactory(s,o,i){var a=makeSequence(s);return a.__iterateUncached=function(a,u){var _=this;if(u)return this.cacheResult().__iterate(a,u);var w=0;return s.__iterate((function(s,u,x){return o.call(i,s,u,x)&&++w&&a(s,u,_)})),w},a.__iteratorUncached=function(a,u){var _=this;if(u)return this.cacheResult().__iterator(a,u);var w=s.__iterator(2,u),x=!0;return new z((function(){if(!x)return{value:void 0,done:!0};var s=w.next();if(s.done)return s;var u=s.value,C=u[0],j=u[1];return o.call(i,j,C,_)?2===a?s:iteratorValue(a,C,j,s):(x=!1,{value:void 0,done:!0})}))},a}(this,s,o))},takeUntil:function takeUntil(s,o){return this.takeWhile(not(s),o)},update:function update(s){return s(this)},valueSeq:function valueSeq(){return this.toIndexedSeq()},hashCode:function hashCode(){return this.__hash||(this.__hash=hashCollection(this))}});var qt=w.prototype;qt[_]=!0,qt[$]=qt.values,qt.toJSON=qt.toArray,qt.__toStringMapper=quoteString,qt.inspect=qt.toSource=function(){return this.toString()},qt.chain=qt.flatMap,qt.contains=qt.includes,mixin(x,{flip:function flip(){return reify(this,flipFactory(this))},mapEntries:function mapEntries(s,o){var i=this,a=0;return reify(this,this.toSeq().map((function(u,_){return s.call(o,[_,u],a++,i)})).fromEntrySeq())},mapKeys:function mapKeys(s,o){var i=this;return reify(this,this.toSeq().flip().map((function(a,u){return s.call(o,a,u,i)})).flip())}});var $t=x.prototype;$t[u]=!0,$t[$]=qt.entries,$t.toJSON=toObject,$t.__toStringMapper=function(s,o){return quoteString(o)+": "+quoteString(s)},mixin(C,{toKeyedSeq:function toKeyedSeq(){return new tt(this,!1)},filter:function filter(s,o){return reify(this,filterFactory(this,s,o,!1))},findIndex:function findIndex(s,o){var i=this.findEntry(s,o);return i?i[0]:-1},indexOf:function indexOf(s){var o=this.keyOf(s);return void 0===o?-1:o},lastIndexOf:function lastIndexOf(s){var o=this.lastKeyOf(s);return void 0===o?-1:o},reverse:function reverse(){return reify(this,reverseFactory(this,!1))},slice:function slice(s,o){return reify(this,sliceFactory(this,s,o,!1))},splice:function splice(s,o){var i=arguments.length;if(o=Math.max(o||0,0),0===i||2===i&&!o)return this;s=resolveBegin(s,s<0?this.count():this.size);var a=this.slice(0,s);return reify(this,1===i?a:a.concat(arrCopy(arguments,2),this.slice(s+o)))},findLastIndex:function findLastIndex(s,o){var i=this.findLastEntry(s,o);return i?i[0]:-1},first:function first(s){return this.get(0,s)},flatten:function flatten(s){return reify(this,flattenFactory(this,s,!1))},get:function get(s,o){return(s=wrapIndex(this,s))<0||this.size===1/0||void 0!==this.size&&s>this.size?o:this.find((function(o,i){return i===s}),void 0,o)},has:function has(s){return(s=wrapIndex(this,s))>=0&&(void 0!==this.size?this.size===1/0||s2?[]:void 0,{"":s})}function fromJSWith(s,o,i,a,u,_){if("string"!=typeof i&&!isImmutable(i)&&(isArrayLike(i)||hasIterator(i)||isPlainObject(i))){if(~s.indexOf(i))throw new TypeError("Cannot convert circular structure to Immutable");s.push(i),u&&""!==a&&u.push(a);var w=o.call(_,a,le(i).map((function(a,_){return fromJSWith(s,o,a,_,u,i)})),u&&u.slice());return s.pop(),u&&u.pop(),w}return i}function defaultConverter(s,o){return isIndexed(o)?o.toList():isKeyed(o)?o.toMap():o.toSet()}var Qt="5.1.9",Zt=w},56698(s){"function"==typeof Object.create?s.exports=function inherits(s,o){o&&(s.super_=o,s.prototype=Object.create(o.prototype,{constructor:{value:s,enumerable:!1,writable:!0,configurable:!0}}))}:s.exports=function inherits(s,o){if(o){s.super_=o;var TempCtor=function(){};TempCtor.prototype=o.prototype,s.prototype=new TempCtor,s.prototype.constructor=s}}},69600(s){"use strict";var o,i,a=Function.prototype.toString,u="object"==typeof Reflect&&null!==Reflect&&Reflect.apply;if("function"==typeof u&&"function"==typeof Object.defineProperty)try{o=Object.defineProperty({},"length",{get:function(){throw i}}),i={},u((function(){throw 42}),null,o)}catch(s){s!==i&&(u=null)}else u=null;var _=/^\s*class\b/,w=function isES6ClassFunction(s){try{var o=a.call(s);return _.test(o)}catch(s){return!1}},x=function tryFunctionToStr(s){try{return!w(s)&&(a.call(s),!0)}catch(s){return!1}},C=Object.prototype.toString,j="function"==typeof Symbol&&!!Symbol.toStringTag,L=!(0 in[,]),B=function isDocumentDotAll(){return!1};if("object"==typeof document){var $=document.all;C.call($)===C.call(document.all)&&(B=function isDocumentDotAll(s){if((L||!s)&&(void 0===s||"object"==typeof s))try{var o=C.call(s);return("[object HTMLAllCollection]"===o||"[object HTML document.all class]"===o||"[object HTMLCollection]"===o||"[object Object]"===o)&&null==s("")}catch(s){}return!1})}s.exports=u?function isCallable(s){if(B(s))return!0;if(!s)return!1;if("function"!=typeof s&&"object"!=typeof s)return!1;try{u(s,null,o)}catch(s){if(s!==i)return!1}return!w(s)&&x(s)}:function isCallable(s){if(B(s))return!0;if(!s)return!1;if("function"!=typeof s&&"object"!=typeof s)return!1;if(j)return x(s);if(w(s))return!1;var o=C.call(s);return!("[object Function]"!==o&&"[object GeneratorFunction]"!==o&&!/^\[object HTML/.test(o))&&x(s)}},35680(s,o,i){"use strict";var a=i(25767);s.exports=function isTypedArray(s){return!!a(s)}},64634(s){var o={}.toString;s.exports=Array.isArray||function(s){return"[object Array]"==o.call(s)}},5419(s){s.exports=function(s,o,i,a){var u=new Blob(void 0!==a?[a,s]:[s],{type:i||"application/octet-stream"});if(void 0!==window.navigator.msSaveBlob)window.navigator.msSaveBlob(u,o);else{var _=window.URL&&window.URL.createObjectURL?window.URL.createObjectURL(u):window.webkitURL.createObjectURL(u),w=document.createElement("a");w.style.display="none",w.href=_,w.setAttribute("download",o),void 0===w.download&&w.setAttribute("target","_blank"),document.body.appendChild(w),w.click(),setTimeout((function(){document.body.removeChild(w),window.URL.revokeObjectURL(_)}),200)}}},20181(s,o,i){var a=/^\s+|\s+$/g,u=/^[-+]0x[0-9a-f]+$/i,_=/^0b[01]+$/i,w=/^0o[0-7]+$/i,x=parseInt,C="object"==typeof i.g&&i.g&&i.g.Object===Object&&i.g,j="object"==typeof self&&self&&self.Object===Object&&self,L=C||j||Function("return this")(),B=Object.prototype.toString,$=Math.max,z=Math.min,now=function(){return L.Date.now()};function isObject(s){var o=typeof s;return!!s&&("object"==o||"function"==o)}function toNumber(s){if("number"==typeof s)return s;if(function isSymbol(s){return"symbol"==typeof s||function isObjectLike(s){return!!s&&"object"==typeof s}(s)&&"[object Symbol]"==B.call(s)}(s))return NaN;if(isObject(s)){var o="function"==typeof s.valueOf?s.valueOf():s;s=isObject(o)?o+"":o}if("string"!=typeof s)return 0===s?s:+s;s=s.replace(a,"");var i=_.test(s);return i||w.test(s)?x(s.slice(2),i?2:8):u.test(s)?NaN:+s}s.exports=function debounce(s,o,i){var a,u,_,w,x,C,j=0,L=!1,B=!1,U=!0;if("function"!=typeof s)throw new TypeError("Expected a function");function invokeFunc(o){var i=a,_=u;return a=u=void 0,j=o,w=s.apply(_,i)}function shouldInvoke(s){var i=s-C;return void 0===C||i>=o||i<0||B&&s-j>=_}function timerExpired(){var s=now();if(shouldInvoke(s))return trailingEdge(s);x=setTimeout(timerExpired,function remainingWait(s){var i=o-(s-C);return B?z(i,_-(s-j)):i}(s))}function trailingEdge(s){return x=void 0,U&&a?invokeFunc(s):(a=u=void 0,w)}function debounced(){var s=now(),i=shouldInvoke(s);if(a=arguments,u=this,C=s,i){if(void 0===x)return function leadingEdge(s){return j=s,x=setTimeout(timerExpired,o),L?invokeFunc(s):w}(C);if(B)return x=setTimeout(timerExpired,o),invokeFunc(C)}return void 0===x&&(x=setTimeout(timerExpired,o)),w}return o=toNumber(o)||0,isObject(i)&&(L=!!i.leading,_=(B="maxWait"in i)?$(toNumber(i.maxWait)||0,o):_,U="trailing"in i?!!i.trailing:U),debounced.cancel=function cancel(){void 0!==x&&clearTimeout(x),j=0,a=C=u=x=void 0},debounced.flush=function flush(){return void 0===x?w:trailingEdge(now())},debounced}},55580(s,o,i){var a=i(56110)(i(9325),"DataView");s.exports=a},21549(s,o,i){var a=i(22032),u=i(63862),_=i(66721),w=i(12749),x=i(35749);function Hash(s){var o=-1,i=null==s?0:s.length;for(this.clear();++o-1}},70695(s,o,i){var a=i(78096),u=i(72428),_=i(56449),w=i(3656),x=i(30361),C=i(37167),j=Object.prototype.hasOwnProperty;s.exports=function arrayLikeKeys(s,o){var i=_(s),L=!i&&u(s),B=!i&&!L&&w(s),$=!i&&!L&&!B&&C(s),z=i||L||B||$,U=z?a(s.length,String):[],V=U.length;for(var Y in s)!o&&!j.call(s,Y)||z&&("length"==Y||B&&("offset"==Y||"parent"==Y)||$&&("buffer"==Y||"byteLength"==Y||"byteOffset"==Y)||x(Y,V))||U.push(Y);return U}},34932(s){s.exports=function arrayMap(s,o){for(var i=-1,a=null==s?0:s.length,u=Array(a);++i0&&i(j)?o>1?baseFlatten(j,o-1,i,_,w):a(w,j):_||(w[w.length]=j)}return w}},86649(s,o,i){var a=i(83221)();s.exports=a},30641(s,o,i){var a=i(86649),u=i(95950);s.exports=function baseForOwn(s,o){return s&&a(s,o,u)}},47422(s,o,i){var a=i(31769),u=i(77797);s.exports=function baseGet(s,o){for(var i=0,_=(o=a(o,s)).length;null!=s&&i<_;)s=s[u(o[i++])];return i&&i==_?s:void 0}},82199(s,o,i){var a=i(14528),u=i(56449);s.exports=function baseGetAllKeys(s,o,i){var _=o(s);return u(s)?_:a(_,i(s))}},72552(s,o,i){var a=i(51873),u=i(659),_=i(59350),w=a?a.toStringTag:void 0;s.exports=function baseGetTag(s){return null==s?void 0===s?"[object Undefined]":"[object Null]":w&&w in Object(s)?u(s):_(s)}},20426(s){var o=Object.prototype.hasOwnProperty;s.exports=function baseHas(s,i){return null!=s&&o.call(s,i)}},28077(s){s.exports=function baseHasIn(s,o){return null!=s&&o in Object(s)}},96131(s,o,i){var a=i(2523),u=i(85463),_=i(76959);s.exports=function baseIndexOf(s,o,i){return o==o?_(s,o,i):a(s,u,i)}},27534(s,o,i){var a=i(72552),u=i(40346);s.exports=function baseIsArguments(s){return u(s)&&"[object Arguments]"==a(s)}},60270(s,o,i){var a=i(87068),u=i(40346);s.exports=function baseIsEqual(s,o,i,_,w){return s===o||(null==s||null==o||!u(s)&&!u(o)?s!=s&&o!=o:a(s,o,i,_,baseIsEqual,w))}},87068(s,o,i){var a=i(37217),u=i(25911),_=i(21986),w=i(50689),x=i(5861),C=i(56449),j=i(3656),L=i(37167),B="[object Arguments]",$="[object Array]",z="[object Object]",U=Object.prototype.hasOwnProperty;s.exports=function baseIsEqualDeep(s,o,i,V,Y,Z){var ee=C(s),ie=C(o),ae=ee?$:x(s),ce=ie?$:x(o),le=(ae=ae==B?z:ae)==z,pe=(ce=ce==B?z:ce)==z,de=ae==ce;if(de&&j(s)){if(!j(o))return!1;ee=!0,le=!1}if(de&&!le)return Z||(Z=new a),ee||L(s)?u(s,o,i,V,Y,Z):_(s,o,ae,i,V,Y,Z);if(!(1&i)){var fe=le&&U.call(s,"__wrapped__"),ye=pe&&U.call(o,"__wrapped__");if(fe||ye){var be=fe?s.value():s,Se=ye?o.value():o;return Z||(Z=new a),Y(be,Se,i,V,Z)}}return!!de&&(Z||(Z=new a),w(s,o,i,V,Y,Z))}},29172(s,o,i){var a=i(5861),u=i(40346);s.exports=function baseIsMap(s){return u(s)&&"[object Map]"==a(s)}},41799(s,o,i){var a=i(37217),u=i(60270);s.exports=function baseIsMatch(s,o,i,_){var w=i.length,x=w,C=!_;if(null==s)return!x;for(s=Object(s);w--;){var j=i[w];if(C&&j[2]?j[1]!==s[j[0]]:!(j[0]in s))return!1}for(;++wu?0:u+o),(i=i>u?u:i)<0&&(i+=u),u=o>i?0:i-o>>>0,o>>>=0;for(var _=Array(u);++a=u?s:a(s,o,i)}},49653(s,o,i){var a=i(37828);s.exports=function cloneArrayBuffer(s){var o=new s.constructor(s.byteLength);return new a(o).set(new a(s)),o}},93290(s,o,i){s=i.nmd(s);var a=i(9325),u=o&&!o.nodeType&&o,_=u&&s&&!s.nodeType&&s,w=_&&_.exports===u?a.Buffer:void 0,x=w?w.allocUnsafe:void 0;s.exports=function cloneBuffer(s,o){if(o)return s.slice();var i=s.length,a=x?x(i):new s.constructor(i);return s.copy(a),a}},76169(s,o,i){var a=i(49653);s.exports=function cloneDataView(s,o){var i=o?a(s.buffer):s.buffer;return new s.constructor(i,s.byteOffset,s.byteLength)}},73201(s){var o=/\w*$/;s.exports=function cloneRegExp(s){var i=new s.constructor(s.source,o.exec(s));return i.lastIndex=s.lastIndex,i}},93736(s,o,i){var a=i(51873),u=a?a.prototype:void 0,_=u?u.valueOf:void 0;s.exports=function cloneSymbol(s){return _?Object(_.call(s)):{}}},71961(s,o,i){var a=i(49653);s.exports=function cloneTypedArray(s,o){var i=o?a(s.buffer):s.buffer;return new s.constructor(i,s.byteOffset,s.length)}},91596(s){var o=Math.max;s.exports=function composeArgs(s,i,a,u){for(var _=-1,w=s.length,x=a.length,C=-1,j=i.length,L=o(w-x,0),B=Array(j+L),$=!u;++C1?i[_-1]:void 0,x=_>2?i[2]:void 0;for(w=s.length>3&&"function"==typeof w?(_--,w):void 0,x&&u(i[0],i[1],x)&&(w=_<3?void 0:w,_=1),o=Object(o);++a<_;){var C=i[a];C&&s(o,C,a,w)}return o}))}},38329(s,o,i){var a=i(64894);s.exports=function createBaseEach(s,o){return function(i,u){if(null==i)return i;if(!a(i))return s(i,u);for(var _=i.length,w=o?_:-1,x=Object(i);(o?w--:++w<_)&&!1!==u(x[w],w,x););return i}}},83221(s){s.exports=function createBaseFor(s){return function(o,i,a){for(var u=-1,_=Object(o),w=a(o),x=w.length;x--;){var C=w[s?x:++u];if(!1===i(_[C],C,_))break}return o}}},11842(s,o,i){var a=i(82819),u=i(9325);s.exports=function createBind(s,o,i){var _=1&o,w=a(s);return function wrapper(){return(this&&this!==u&&this instanceof wrapper?w:s).apply(_?i:this,arguments)}}},12507(s,o,i){var a=i(28754),u=i(49698),_=i(63912),w=i(13222);s.exports=function createCaseFirst(s){return function(o){o=w(o);var i=u(o)?_(o):void 0,x=i?i[0]:o.charAt(0),C=i?a(i,1).join(""):o.slice(1);return x[s]()+C}}},45539(s,o,i){var a=i(40882),u=i(50828),_=i(66645),w=RegExp("['’]","g");s.exports=function createCompounder(s){return function(o){return a(_(u(o).replace(w,"")),s,"")}}},82819(s,o,i){var a=i(39344),u=i(23805);s.exports=function createCtor(s){return function(){var o=arguments;switch(o.length){case 0:return new s;case 1:return new s(o[0]);case 2:return new s(o[0],o[1]);case 3:return new s(o[0],o[1],o[2]);case 4:return new s(o[0],o[1],o[2],o[3]);case 5:return new s(o[0],o[1],o[2],o[3],o[4]);case 6:return new s(o[0],o[1],o[2],o[3],o[4],o[5]);case 7:return new s(o[0],o[1],o[2],o[3],o[4],o[5],o[6])}var i=a(s.prototype),_=s.apply(i,o);return u(_)?_:i}}},77078(s,o,i){var a=i(91033),u=i(82819),_=i(37471),w=i(18073),x=i(11287),C=i(36306),j=i(9325);s.exports=function createCurry(s,o,i){var L=u(s);return function wrapper(){for(var u=arguments.length,B=Array(u),$=u,z=x(wrapper);$--;)B[$]=arguments[$];var U=u<3&&B[0]!==z&&B[u-1]!==z?[]:C(B,z);return(u-=U.length)-1?x[C?o[j]:j]:void 0}}},37471(s,o,i){var a=i(91596),u=i(53320),_=i(58523),w=i(82819),x=i(18073),C=i(11287),j=i(68294),L=i(36306),B=i(9325);s.exports=function createHybrid(s,o,i,$,z,U,V,Y,Z,ee){var ie=128&o,ae=1&o,ce=2&o,le=24&o,pe=512&o,de=ce?void 0:w(s);return function wrapper(){for(var fe=arguments.length,ye=Array(fe),be=fe;be--;)ye[be]=arguments[be];if(le)var Se=C(wrapper),_e=_(ye,Se);if($&&(ye=a(ye,$,z,le)),U&&(ye=u(ye,U,V,le)),fe-=_e,le&&fe1&&ye.reverse(),ie&&ZL))return!1;var $=C.get(s),z=C.get(o);if($&&z)return $==o&&z==s;var U=-1,V=!0,Y=2&i?new a:void 0;for(C.set(s,o),C.set(o,s);++U1?"& ":"")+i[u],i=i.join(a>2?", ":" "),s.replace(o,"{\n/* [wrapped with "+i+"] */\n")}},45891(s,o,i){var a=i(51873),u=i(72428),_=i(56449),w=a?a.isConcatSpreadable:void 0;s.exports=function isFlattenable(s){return _(s)||u(s)||!!(w&&s&&s[w])}},30361(s){var o=/^(?:0|[1-9]\d*)$/;s.exports=function isIndex(s,i){var a=typeof s;return!!(i=null==i?9007199254740991:i)&&("number"==a||"symbol"!=a&&o.test(s))&&s>-1&&s%1==0&&s-1}},31175(s,o,i){var a=i(26025);s.exports=function listCacheSet(s,o){var i=this.__data__,u=a(i,s);return u<0?(++this.size,i.push([s,o])):i[u][1]=o,this}},63040(s,o,i){var a=i(21549),u=i(80079),_=i(68223);s.exports=function mapCacheClear(){this.size=0,this.__data__={hash:new a,map:new(_||u),string:new a}}},17670(s,o,i){var a=i(12651);s.exports=function mapCacheDelete(s){var o=a(this,s).delete(s);return this.size-=o?1:0,o}},90289(s,o,i){var a=i(12651);s.exports=function mapCacheGet(s){return a(this,s).get(s)}},4509(s,o,i){var a=i(12651);s.exports=function mapCacheHas(s){return a(this,s).has(s)}},72949(s,o,i){var a=i(12651);s.exports=function mapCacheSet(s,o){var i=a(this,s),u=i.size;return i.set(s,o),this.size+=i.size==u?0:1,this}},20317(s){s.exports=function mapToArray(s){var o=-1,i=Array(s.size);return s.forEach((function(s,a){i[++o]=[a,s]})),i}},67197(s){s.exports=function matchesStrictComparable(s,o){return function(i){return null!=i&&(i[s]===o&&(void 0!==o||s in Object(i)))}}},62224(s,o,i){var a=i(50104);s.exports=function memoizeCapped(s){var o=a(s,(function(s){return 500===i.size&&i.clear(),s})),i=o.cache;return o}},3209(s,o,i){var a=i(91596),u=i(53320),_=i(36306),w="__lodash_placeholder__",x=128,C=Math.min;s.exports=function mergeData(s,o){var i=s[1],j=o[1],L=i|j,B=L<131,$=j==x&&8==i||j==x&&256==i&&s[7].length<=o[8]||384==j&&o[7].length<=o[8]&&8==i;if(!B&&!$)return s;1&j&&(s[2]=o[2],L|=1&i?0:4);var z=o[3];if(z){var U=s[3];s[3]=U?a(U,z,o[4]):z,s[4]=U?_(s[3],w):o[4]}return(z=o[5])&&(U=s[5],s[5]=U?u(U,z,o[6]):z,s[6]=U?_(s[5],w):o[6]),(z=o[7])&&(s[7]=z),j&x&&(s[8]=null==s[8]?o[8]:C(s[8],o[8])),null==s[9]&&(s[9]=o[9]),s[0]=o[0],s[1]=L,s}},48152(s,o,i){var a=i(28303),u=a&&new a;s.exports=u},81042(s,o,i){var a=i(56110)(Object,"create");s.exports=a},3650(s,o,i){var a=i(74335)(Object.keys,Object);s.exports=a},90181(s){s.exports=function nativeKeysIn(s){var o=[];if(null!=s)for(var i in Object(s))o.push(i);return o}},86009(s,o,i){s=i.nmd(s);var a=i(34840),u=o&&!o.nodeType&&o,_=u&&s&&!s.nodeType&&s,w=_&&_.exports===u&&a.process,x=function(){try{var s=_&&_.require&&_.require("util").types;return s||w&&w.binding&&w.binding("util")}catch(s){}}();s.exports=x},59350(s){var o=Object.prototype.toString;s.exports=function objectToString(s){return o.call(s)}},74335(s){s.exports=function overArg(s,o){return function(i){return s(o(i))}}},56757(s,o,i){var a=i(91033),u=Math.max;s.exports=function overRest(s,o,i){return o=u(void 0===o?s.length-1:o,0),function(){for(var _=arguments,w=-1,x=u(_.length-o,0),C=Array(x);++w0){if(++i>=800)return arguments[0]}else i=0;return s.apply(void 0,arguments)}}},51420(s,o,i){var a=i(80079);s.exports=function stackClear(){this.__data__=new a,this.size=0}},90938(s){s.exports=function stackDelete(s){var o=this.__data__,i=o.delete(s);return this.size=o.size,i}},63605(s){s.exports=function stackGet(s){return this.__data__.get(s)}},29817(s){s.exports=function stackHas(s){return this.__data__.has(s)}},80945(s,o,i){var a=i(80079),u=i(68223),_=i(53661);s.exports=function stackSet(s,o){var i=this.__data__;if(i instanceof a){var w=i.__data__;if(!u||w.length<199)return w.push([s,o]),this.size=++i.size,this;i=this.__data__=new _(w)}return i.set(s,o),this.size=i.size,this}},76959(s){s.exports=function strictIndexOf(s,o,i){for(var a=i-1,u=s.length;++a=o||i<0||Y&&s-U>=L}function timerExpired(){var s=u();if(shouldInvoke(s))return trailingEdge(s);$=setTimeout(timerExpired,function remainingWait(s){var i=o-(s-z);return Y?x(i,L-(s-U)):i}(s))}function trailingEdge(s){return $=void 0,Z&&C?invokeFunc(s):(C=j=void 0,B)}function debounced(){var s=u(),i=shouldInvoke(s);if(C=arguments,j=this,z=s,i){if(void 0===$)return function leadingEdge(s){return U=s,$=setTimeout(timerExpired,o),V?invokeFunc(s):B}(z);if(Y)return clearTimeout($),$=setTimeout(timerExpired,o),invokeFunc(z)}return void 0===$&&($=setTimeout(timerExpired,o)),B}return o=_(o)||0,a(i)&&(V=!!i.leading,L=(Y="maxWait"in i)?w(_(i.maxWait)||0,o):L,Z="trailing"in i?!!i.trailing:Z),debounced.cancel=function cancel(){void 0!==$&&clearTimeout($),U=0,C=z=j=$=void 0},debounced.flush=function flush(){return void 0===$?B:trailingEdge(u())},debounced}},50828(s,o,i){var a=i(24647),u=i(13222),_=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,w=RegExp("[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]","g");s.exports=function deburr(s){return(s=u(s))&&s.replace(_,a).replace(w,"")}},75288(s){s.exports=function eq(s,o){return s===o||s!=s&&o!=o}},60680(s,o,i){var a=i(13222),u=/[\\^$.*+?()[\]{}|]/g,_=RegExp(u.source);s.exports=function escapeRegExp(s){return(s=a(s))&&_.test(s)?s.replace(u,"\\$&"):s}},7309(s,o,i){var a=i(62006)(i(24713));s.exports=a},24713(s,o,i){var a=i(2523),u=i(15389),_=i(61489),w=Math.max;s.exports=function findIndex(s,o,i){var x=null==s?0:s.length;if(!x)return-1;var C=null==i?0:_(i);return C<0&&(C=w(x+C,0)),a(s,u(o,3),C)}},35970(s,o,i){var a=i(83120);s.exports=function flatten(s){return(null==s?0:s.length)?a(s,1):[]}},73424(s,o,i){var a=i(16962),u=i(2874),_=Array.prototype.push;function baseAry(s,o){return 2==o?function(o,i){return s(o,i)}:function(o){return s(o)}}function cloneArray(s){for(var o=s?s.length:0,i=Array(o);o--;)i[o]=s[o];return i}function wrapImmutable(s,o){return function(){var i=arguments.length;if(i){for(var a=Array(i);i--;)a[i]=arguments[i];var u=a[0]=o.apply(void 0,a);return s.apply(void 0,a),u}}}s.exports=function baseConvert(s,o,i,w){var x="function"==typeof o,C=o===Object(o);if(C&&(w=i,i=o,o=void 0),null==i)throw new TypeError;w||(w={});var j=!("cap"in w)||w.cap,L=!("curry"in w)||w.curry,B=!("fixed"in w)||w.fixed,$=!("immutable"in w)||w.immutable,z=!("rearg"in w)||w.rearg,U=x?i:u,V="curry"in w&&w.curry,Y="fixed"in w&&w.fixed,Z="rearg"in w&&w.rearg,ee=x?i.runInContext():void 0,ie=x?i:{ary:s.ary,assign:s.assign,clone:s.clone,curry:s.curry,forEach:s.forEach,isArray:s.isArray,isError:s.isError,isFunction:s.isFunction,isWeakMap:s.isWeakMap,iteratee:s.iteratee,keys:s.keys,rearg:s.rearg,toInteger:s.toInteger,toPath:s.toPath},ae=ie.ary,ce=ie.assign,le=ie.clone,pe=ie.curry,de=ie.forEach,fe=ie.isArray,ye=ie.isError,be=ie.isFunction,Se=ie.isWeakMap,_e=ie.keys,we=ie.rearg,xe=ie.toInteger,Pe=ie.toPath,Te=_e(a.aryMethod),Re={castArray:function(s){return function(){var o=arguments[0];return fe(o)?s(cloneArray(o)):s.apply(void 0,arguments)}},iteratee:function(s){return function(){var o=arguments[1],i=s(arguments[0],o),a=i.length;return j&&"number"==typeof o?(o=o>2?o-2:1,a&&a<=o?i:baseAry(i,o)):i}},mixin:function(s){return function(o){var i=this;if(!be(i))return s(i,Object(o));var a=[];return de(_e(o),(function(s){be(o[s])&&a.push([s,i.prototype[s]])})),s(i,Object(o)),de(a,(function(s){var o=s[1];be(o)?i.prototype[s[0]]=o:delete i.prototype[s[0]]})),i}},nthArg:function(s){return function(o){var i=o<0?1:xe(o)+1;return pe(s(o),i)}},rearg:function(s){return function(o,i){var a=i?i.length:0;return pe(s(o,i),a)}},runInContext:function(o){return function(i){return baseConvert(s,o(i),w)}}};function castCap(s,o){if(j){var i=a.iterateeRearg[s];if(i)return function iterateeRearg(s,o){return overArg(s,(function(s){var i=o.length;return function baseArity(s,o){return 2==o?function(o,i){return s.apply(void 0,arguments)}:function(o){return s.apply(void 0,arguments)}}(we(baseAry(s,i),o),i)}))}(o,i);var u=!x&&a.iterateeAry[s];if(u)return function iterateeAry(s,o){return overArg(s,(function(s){return"function"==typeof s?baseAry(s,o):s}))}(o,u)}return o}function castFixed(s,o,i){if(B&&(Y||!a.skipFixed[s])){var u=a.methodSpread[s],w=u&&u.start;return void 0===w?ae(o,i):function flatSpread(s,o){return function(){for(var i=arguments.length,a=i-1,u=Array(i);i--;)u[i]=arguments[i];var w=u[o],x=u.slice(0,o);return w&&_.apply(x,w),o!=a&&_.apply(x,u.slice(o+1)),s.apply(this,x)}}(o,w)}return o}function castRearg(s,o,i){return z&&i>1&&(Z||!a.skipRearg[s])?we(o,a.methodRearg[s]||a.aryRearg[i]):o}function cloneByPath(s,o){for(var i=-1,a=(o=Pe(o)).length,u=a-1,_=le(Object(s)),w=_;null!=w&&++i1?pe(o,i):o}(0,u=castCap(_,u),s),!1}})),!u})),u||(u=w),u==o&&(u=V?pe(u,1):function(){return o.apply(this,arguments)}),u.convert=createConverter(_,o),u.placeholder=o.placeholder=i,u}if(!C)return wrap(o,i,U);var qe=i,$e=[];return de(Te,(function(s){de(a.aryMethod[s],(function(s){var o=qe[a.remap[s]||s];o&&$e.push([s,wrap(s,o,qe)])}))})),de(_e(qe),(function(s){var o=qe[s];if("function"==typeof o){for(var i=$e.length;i--;)if($e[i][0]==s)return;o.convert=createConverter(s,o),$e.push([s,o])}})),de($e,(function(s){qe[s[0]]=s[1]})),qe.convert=function convertLib(s){return qe.runInContext.convert(s)(void 0)},qe.placeholder=qe,de(_e(qe),(function(s){de(a.realToAlias[s]||[],(function(o){qe[o]=qe[s]}))})),qe}},16962(s,o){o.aliasToReal={each:"forEach",eachRight:"forEachRight",entries:"toPairs",entriesIn:"toPairsIn",extend:"assignIn",extendAll:"assignInAll",extendAllWith:"assignInAllWith",extendWith:"assignInWith",first:"head",conforms:"conformsTo",matches:"isMatch",property:"get",__:"placeholder",F:"stubFalse",T:"stubTrue",all:"every",allPass:"overEvery",always:"constant",any:"some",anyPass:"overSome",apply:"spread",assoc:"set",assocPath:"set",complement:"negate",compose:"flowRight",contains:"includes",dissoc:"unset",dissocPath:"unset",dropLast:"dropRight",dropLastWhile:"dropRightWhile",equals:"isEqual",identical:"eq",indexBy:"keyBy",init:"initial",invertObj:"invert",juxt:"over",omitAll:"omit",nAry:"ary",path:"get",pathEq:"matchesProperty",pathOr:"getOr",paths:"at",pickAll:"pick",pipe:"flow",pluck:"map",prop:"get",propEq:"matchesProperty",propOr:"getOr",props:"at",symmetricDifference:"xor",symmetricDifferenceBy:"xorBy",symmetricDifferenceWith:"xorWith",takeLast:"takeRight",takeLastWhile:"takeRightWhile",unapply:"rest",unnest:"flatten",useWith:"overArgs",where:"conformsTo",whereEq:"isMatch",zipObj:"zipObject"},o.aryMethod={1:["assignAll","assignInAll","attempt","castArray","ceil","create","curry","curryRight","defaultsAll","defaultsDeepAll","floor","flow","flowRight","fromPairs","invert","iteratee","memoize","method","mergeAll","methodOf","mixin","nthArg","over","overEvery","overSome","rest","reverse","round","runInContext","spread","template","trim","trimEnd","trimStart","uniqueId","words","zipAll"],2:["add","after","ary","assign","assignAllWith","assignIn","assignInAllWith","at","before","bind","bindAll","bindKey","chunk","cloneDeepWith","cloneWith","concat","conformsTo","countBy","curryN","curryRightN","debounce","defaults","defaultsDeep","defaultTo","delay","difference","divide","drop","dropRight","dropRightWhile","dropWhile","endsWith","eq","every","filter","find","findIndex","findKey","findLast","findLastIndex","findLastKey","flatMap","flatMapDeep","flattenDepth","forEach","forEachRight","forIn","forInRight","forOwn","forOwnRight","get","groupBy","gt","gte","has","hasIn","includes","indexOf","intersection","invertBy","invoke","invokeMap","isEqual","isMatch","join","keyBy","lastIndexOf","lt","lte","map","mapKeys","mapValues","matchesProperty","maxBy","meanBy","merge","mergeAllWith","minBy","multiply","nth","omit","omitBy","overArgs","pad","padEnd","padStart","parseInt","partial","partialRight","partition","pick","pickBy","propertyOf","pull","pullAll","pullAt","random","range","rangeRight","rearg","reject","remove","repeat","restFrom","result","sampleSize","some","sortBy","sortedIndex","sortedIndexOf","sortedLastIndex","sortedLastIndexOf","sortedUniqBy","split","spreadFrom","startsWith","subtract","sumBy","take","takeRight","takeRightWhile","takeWhile","tap","throttle","thru","times","trimChars","trimCharsEnd","trimCharsStart","truncate","union","uniqBy","uniqWith","unset","unzipWith","without","wrap","xor","zip","zipObject","zipObjectDeep"],3:["assignInWith","assignWith","clamp","differenceBy","differenceWith","findFrom","findIndexFrom","findLastFrom","findLastIndexFrom","getOr","includesFrom","indexOfFrom","inRange","intersectionBy","intersectionWith","invokeArgs","invokeArgsMap","isEqualWith","isMatchWith","flatMapDepth","lastIndexOfFrom","mergeWith","orderBy","padChars","padCharsEnd","padCharsStart","pullAllBy","pullAllWith","rangeStep","rangeStepRight","reduce","reduceRight","replace","set","slice","sortedIndexBy","sortedLastIndexBy","transform","unionBy","unionWith","update","xorBy","xorWith","zipWith"],4:["fill","setWith","updateWith"]},o.aryRearg={2:[1,0],3:[2,0,1],4:[3,2,0,1]},o.iterateeAry={dropRightWhile:1,dropWhile:1,every:1,filter:1,find:1,findFrom:1,findIndex:1,findIndexFrom:1,findKey:1,findLast:1,findLastFrom:1,findLastIndex:1,findLastIndexFrom:1,findLastKey:1,flatMap:1,flatMapDeep:1,flatMapDepth:1,forEach:1,forEachRight:1,forIn:1,forInRight:1,forOwn:1,forOwnRight:1,map:1,mapKeys:1,mapValues:1,partition:1,reduce:2,reduceRight:2,reject:1,remove:1,some:1,takeRightWhile:1,takeWhile:1,times:1,transform:2},o.iterateeRearg={mapKeys:[1],reduceRight:[1,0]},o.methodRearg={assignInAllWith:[1,0],assignInWith:[1,2,0],assignAllWith:[1,0],assignWith:[1,2,0],differenceBy:[1,2,0],differenceWith:[1,2,0],getOr:[2,1,0],intersectionBy:[1,2,0],intersectionWith:[1,2,0],isEqualWith:[1,2,0],isMatchWith:[2,1,0],mergeAllWith:[1,0],mergeWith:[1,2,0],padChars:[2,1,0],padCharsEnd:[2,1,0],padCharsStart:[2,1,0],pullAllBy:[2,1,0],pullAllWith:[2,1,0],rangeStep:[1,2,0],rangeStepRight:[1,2,0],setWith:[3,1,2,0],sortedIndexBy:[2,1,0],sortedLastIndexBy:[2,1,0],unionBy:[1,2,0],unionWith:[1,2,0],updateWith:[3,1,2,0],xorBy:[1,2,0],xorWith:[1,2,0],zipWith:[1,2,0]},o.methodSpread={assignAll:{start:0},assignAllWith:{start:0},assignInAll:{start:0},assignInAllWith:{start:0},defaultsAll:{start:0},defaultsDeepAll:{start:0},invokeArgs:{start:2},invokeArgsMap:{start:2},mergeAll:{start:0},mergeAllWith:{start:0},partial:{start:1},partialRight:{start:1},without:{start:1},zipAll:{start:0}},o.mutate={array:{fill:!0,pull:!0,pullAll:!0,pullAllBy:!0,pullAllWith:!0,pullAt:!0,remove:!0,reverse:!0},object:{assign:!0,assignAll:!0,assignAllWith:!0,assignIn:!0,assignInAll:!0,assignInAllWith:!0,assignInWith:!0,assignWith:!0,defaults:!0,defaultsAll:!0,defaultsDeep:!0,defaultsDeepAll:!0,merge:!0,mergeAll:!0,mergeAllWith:!0,mergeWith:!0},set:{set:!0,setWith:!0,unset:!0,update:!0,updateWith:!0}},o.realToAlias=function(){var s=Object.prototype.hasOwnProperty,i=o.aliasToReal,a={};for(var u in i){var _=i[u];s.call(a,_)?a[_].push(u):a[_]=[u]}return a}(),o.remap={assignAll:"assign",assignAllWith:"assignWith",assignInAll:"assignIn",assignInAllWith:"assignInWith",curryN:"curry",curryRightN:"curryRight",defaultsAll:"defaults",defaultsDeepAll:"defaultsDeep",findFrom:"find",findIndexFrom:"findIndex",findLastFrom:"findLast",findLastIndexFrom:"findLastIndex",getOr:"get",includesFrom:"includes",indexOfFrom:"indexOf",invokeArgs:"invoke",invokeArgsMap:"invokeMap",lastIndexOfFrom:"lastIndexOf",mergeAll:"merge",mergeAllWith:"mergeWith",padChars:"pad",padCharsEnd:"padEnd",padCharsStart:"padStart",propertyOf:"get",rangeStep:"range",rangeStepRight:"rangeRight",restFrom:"rest",spreadFrom:"spread",trimChars:"trim",trimCharsEnd:"trimEnd",trimCharsStart:"trimStart",zipAll:"zip"},o.skipFixed={castArray:!0,flow:!0,flowRight:!0,iteratee:!0,mixin:!0,rearg:!0,runInContext:!0},o.skipRearg={add:!0,assign:!0,assignIn:!0,bind:!0,bindKey:!0,concat:!0,difference:!0,divide:!0,eq:!0,gt:!0,gte:!0,isEqual:!0,lt:!0,lte:!0,matchesProperty:!0,merge:!0,multiply:!0,overArgs:!0,partial:!0,partialRight:!0,propertyOf:!0,random:!0,range:!0,rangeRight:!0,subtract:!0,zip:!0,zipObject:!0,zipObjectDeep:!0}},47934(s,o,i){s.exports={ary:i(64626),assign:i(74733),clone:i(32629),curry:i(49747),forEach:i(83729),isArray:i(56449),isError:i(23546),isFunction:i(1882),isWeakMap:i(47886),iteratee:i(33855),keys:i(88984),rearg:i(84195),toInteger:i(61489),toPath:i(42072)}},56367(s,o,i){s.exports=i(77731)},79920(s,o,i){var a=i(73424),u=i(47934);s.exports=function convert(s,o,i){return a(u,s,o,i)}},2874(s){s.exports={}},77731(s,o,i){var a=i(79920)("set",i(63560));a.placeholder=i(2874),s.exports=a},58156(s,o,i){var a=i(47422);s.exports=function get(s,o,i){var u=null==s?void 0:a(s,o);return void 0===u?i:u}},61448(s,o,i){var a=i(20426),u=i(49326);s.exports=function has(s,o){return null!=s&&u(s,o,a)}},80631(s,o,i){var a=i(28077),u=i(49326);s.exports=function hasIn(s,o){return null!=s&&u(s,o,a)}},83488(s){s.exports=function identity(s){return s}},72428(s,o,i){var a=i(27534),u=i(40346),_=Object.prototype,w=_.hasOwnProperty,x=_.propertyIsEnumerable,C=a(function(){return arguments}())?a:function(s){return u(s)&&w.call(s,"callee")&&!x.call(s,"callee")};s.exports=C},56449(s){var o=Array.isArray;s.exports=o},64894(s,o,i){var a=i(1882),u=i(30294);s.exports=function isArrayLike(s){return null!=s&&u(s.length)&&!a(s)}},83693(s,o,i){var a=i(64894),u=i(40346);s.exports=function isArrayLikeObject(s){return u(s)&&a(s)}},53812(s,o,i){var a=i(72552),u=i(40346);s.exports=function isBoolean(s){return!0===s||!1===s||u(s)&&"[object Boolean]"==a(s)}},3656(s,o,i){s=i.nmd(s);var a=i(9325),u=i(89935),_=o&&!o.nodeType&&o,w=_&&s&&!s.nodeType&&s,x=w&&w.exports===_?a.Buffer:void 0,C=(x?x.isBuffer:void 0)||u;s.exports=C},62193(s,o,i){var a=i(88984),u=i(5861),_=i(72428),w=i(56449),x=i(64894),C=i(3656),j=i(55527),L=i(37167),B=Object.prototype.hasOwnProperty;s.exports=function isEmpty(s){if(null==s)return!0;if(x(s)&&(w(s)||"string"==typeof s||"function"==typeof s.splice||C(s)||L(s)||_(s)))return!s.length;var o=u(s);if("[object Map]"==o||"[object Set]"==o)return!s.size;if(j(s))return!a(s).length;for(var i in s)if(B.call(s,i))return!1;return!0}},2404(s,o,i){var a=i(60270);s.exports=function isEqual(s,o){return a(s,o)}},23546(s,o,i){var a=i(72552),u=i(40346),_=i(11331);s.exports=function isError(s){if(!u(s))return!1;var o=a(s);return"[object Error]"==o||"[object DOMException]"==o||"string"==typeof s.message&&"string"==typeof s.name&&!_(s)}},1882(s,o,i){var a=i(72552),u=i(23805);s.exports=function isFunction(s){if(!u(s))return!1;var o=a(s);return"[object Function]"==o||"[object GeneratorFunction]"==o||"[object AsyncFunction]"==o||"[object Proxy]"==o}},30294(s){s.exports=function isLength(s){return"number"==typeof s&&s>-1&&s%1==0&&s<=9007199254740991}},87730(s,o,i){var a=i(29172),u=i(27301),_=i(86009),w=_&&_.isMap,x=w?u(w):a;s.exports=x},5187(s){s.exports=function isNull(s){return null===s}},98023(s,o,i){var a=i(72552),u=i(40346);s.exports=function isNumber(s){return"number"==typeof s||u(s)&&"[object Number]"==a(s)}},23805(s){s.exports=function isObject(s){var o=typeof s;return null!=s&&("object"==o||"function"==o)}},40346(s){s.exports=function isObjectLike(s){return null!=s&&"object"==typeof s}},11331(s,o,i){var a=i(72552),u=i(28879),_=i(40346),w=Function.prototype,x=Object.prototype,C=w.toString,j=x.hasOwnProperty,L=C.call(Object);s.exports=function isPlainObject(s){if(!_(s)||"[object Object]"!=a(s))return!1;var o=u(s);if(null===o)return!0;var i=j.call(o,"constructor")&&o.constructor;return"function"==typeof i&&i instanceof i&&C.call(i)==L}},38440(s,o,i){var a=i(16038),u=i(27301),_=i(86009),w=_&&_.isSet,x=w?u(w):a;s.exports=x},85015(s,o,i){var a=i(72552),u=i(56449),_=i(40346);s.exports=function isString(s){return"string"==typeof s||!u(s)&&_(s)&&"[object String]"==a(s)}},44394(s,o,i){var a=i(72552),u=i(40346);s.exports=function isSymbol(s){return"symbol"==typeof s||u(s)&&"[object Symbol]"==a(s)}},37167(s,o,i){var a=i(4901),u=i(27301),_=i(86009),w=_&&_.isTypedArray,x=w?u(w):a;s.exports=x},47886(s,o,i){var a=i(5861),u=i(40346);s.exports=function isWeakMap(s){return u(s)&&"[object WeakMap]"==a(s)}},33855(s,o,i){var a=i(9999),u=i(15389);s.exports=function iteratee(s){return u("function"==typeof s?s:a(s,1))}},95950(s,o,i){var a=i(70695),u=i(88984),_=i(64894);s.exports=function keys(s){return _(s)?a(s):u(s)}},37241(s,o,i){var a=i(70695),u=i(72903),_=i(64894);s.exports=function keysIn(s){return _(s)?a(s,!0):u(s)}},68090(s){s.exports=function last(s){var o=null==s?0:s.length;return o?s[o-1]:void 0}},50104(s,o,i){var a=i(53661);function memoize(s,o){if("function"!=typeof s||null!=o&&"function"!=typeof o)throw new TypeError("Expected a function");var memoized=function(){var i=arguments,a=o?o.apply(this,i):i[0],u=memoized.cache;if(u.has(a))return u.get(a);var _=s.apply(this,i);return memoized.cache=u.set(a,_)||u,_};return memoized.cache=new(memoize.Cache||a),memoized}memoize.Cache=a,s.exports=memoize},55364(s,o,i){var a=i(85250),u=i(20999)((function(s,o,i){a(s,o,i)}));s.exports=u},6048(s){s.exports=function negate(s){if("function"!=typeof s)throw new TypeError("Expected a function");return function(){var o=arguments;switch(o.length){case 0:return!s.call(this);case 1:return!s.call(this,o[0]);case 2:return!s.call(this,o[0],o[1]);case 3:return!s.call(this,o[0],o[1],o[2])}return!s.apply(this,o)}}},63950(s){s.exports=function noop(){}},10124(s,o,i){var a=i(9325);s.exports=function(){return a.Date.now()}},90179(s,o,i){var a=i(34932),u=i(9999),_=i(19931),w=i(31769),x=i(21791),C=i(53138),j=i(38816),L=i(83349),B=j((function(s,o){var i={};if(null==s)return i;var j=!1;o=a(o,(function(o){return o=w(o,s),j||(j=o.length>1),o})),x(s,L(s),i),j&&(i=u(i,7,C));for(var B=o.length;B--;)_(i,o[B]);return i}));s.exports=B},50583(s,o,i){var a=i(47237),u=i(17255),_=i(28586),w=i(77797);s.exports=function property(s){return _(s)?a(w(s)):u(s)}},84195(s,o,i){var a=i(66977),u=i(38816),_=u((function(s,o){return a(s,256,void 0,void 0,void 0,o)}));s.exports=_},40860(s,o,i){var a=i(40882),u=i(80909),_=i(15389),w=i(85558),x=i(56449);s.exports=function reduce(s,o,i){var C=x(s)?a:w,j=arguments.length<3;return C(s,_(o,4),i,j,u)}},63560(s,o,i){var a=i(73170);s.exports=function set(s,o,i){return null==s?s:a(s,o,i)}},42426(s,o,i){var a=i(14248),u=i(15389),_=i(90916),w=i(56449),x=i(36800);s.exports=function some(s,o,i){var C=w(s)?a:_;return i&&x(s,o,i)&&(o=void 0),C(s,u(o,3))}},63345(s){s.exports=function stubArray(){return[]}},89935(s){s.exports=function stubFalse(){return!1}},17400(s,o,i){var a=i(99374),u=1/0;s.exports=function toFinite(s){return s?(s=a(s))===u||s===-1/0?17976931348623157e292*(s<0?-1:1):s==s?s:0:0===s?s:0}},61489(s,o,i){var a=i(17400);s.exports=function toInteger(s){var o=a(s),i=o%1;return o==o?i?o-i:o:0}},80218(s,o,i){var a=i(13222);s.exports=function toLower(s){return a(s).toLowerCase()}},99374(s,o,i){var a=i(54128),u=i(23805),_=i(44394),w=/^[-+]0x[0-9a-f]+$/i,x=/^0b[01]+$/i,C=/^0o[0-7]+$/i,j=parseInt;s.exports=function toNumber(s){if("number"==typeof s)return s;if(_(s))return NaN;if(u(s)){var o="function"==typeof s.valueOf?s.valueOf():s;s=u(o)?o+"":o}if("string"!=typeof s)return 0===s?s:+s;s=a(s);var i=x.test(s);return i||C.test(s)?j(s.slice(2),i?2:8):w.test(s)?NaN:+s}},42072(s,o,i){var a=i(34932),u=i(23007),_=i(56449),w=i(44394),x=i(61802),C=i(77797),j=i(13222);s.exports=function toPath(s){return _(s)?a(s,C):w(s)?[s]:u(x(j(s)))}},69884(s,o,i){var a=i(21791),u=i(37241);s.exports=function toPlainObject(s){return a(s,u(s))}},13222(s,o,i){var a=i(77556);s.exports=function toString(s){return null==s?"":a(s)}},55808(s,o,i){var a=i(12507)("toUpperCase");s.exports=a},66645(s,o,i){var a=i(1733),u=i(45434),_=i(13222),w=i(22225);s.exports=function words(s,o,i){return s=_(s),void 0===(o=i?void 0:o)?u(s)?w(s):a(s):s.match(o)||[]}},53758(s,o,i){var a=i(30980),u=i(56017),_=i(94033),w=i(56449),x=i(40346),C=i(80257),j=Object.prototype.hasOwnProperty;function lodash(s){if(x(s)&&!w(s)&&!(s instanceof a)){if(s instanceof u)return s;if(j.call(s,"__wrapped__"))return C(s)}return new u(s)}lodash.prototype=_.prototype,lodash.prototype.constructor=lodash,s.exports=lodash},47248(s,o,i){var a=i(16547),u=i(51234);s.exports=function zipObject(s,o){return u(s||[],o||[],a)}},43768(s,o,i){"use strict";var a=i(45981),u=i(85587);o.highlight=highlight,o.highlightAuto=function highlightAuto(s,o){var i,w,x,C,j=o||{},L=j.subset||a.listLanguages(),B=j.prefix,$=L.length,z=-1;null==B&&(B=_);if("string"!=typeof s)throw u("Expected `string` for value, got `%s`",s);w={relevance:0,language:null,value:[]},i={relevance:0,language:null,value:[]};for(;++z<$;)C=L[z],a.getLanguage(C)&&((x=highlight(C,s,o)).language=C,x.relevance>w.relevance&&(w=x),x.relevance>i.relevance&&(w=i,i=x));w.language&&(i.secondBest=w);return i},o.registerLanguage=function registerLanguage(s,o){a.registerLanguage(s,o)},o.listLanguages=function listLanguages(){return a.listLanguages()},o.registerAlias=function registerAlias(s,o){var i,u=s;o&&((u={})[s]=o);for(i in u)a.registerAliases(u[i],{languageName:i})},Emitter.prototype.addText=function text(s){var o,i,a=this.stack;if(""===s)return;o=a[a.length-1],(i=o.children[o.children.length-1])&&"text"===i.type?i.value+=s:o.children.push({type:"text",value:s})},Emitter.prototype.addKeyword=function addKeyword(s,o){this.openNode(o),this.addText(s),this.closeNode()},Emitter.prototype.addSublanguage=function addSublanguage(s,o){var i=this.stack,a=i[i.length-1],u=s.rootNode.children,_=o?{type:"element",tagName:"span",properties:{className:[o]},children:u}:u;a.children=a.children.concat(_)},Emitter.prototype.openNode=function open(s){var o=this.stack,i=this.options.classPrefix+s,a=o[o.length-1],u={type:"element",tagName:"span",properties:{className:[i]},children:[]};a.children.push(u),o.push(u)},Emitter.prototype.closeNode=function close(){this.stack.pop()},Emitter.prototype.closeAllNodes=noop,Emitter.prototype.finalize=noop,Emitter.prototype.toHTML=function toHtmlNoop(){return""};var _="hljs-";function highlight(s,o,i){var w,x=a.configure({}),C=(i||{}).prefix;if("string"!=typeof s)throw u("Expected `string` for name, got `%s`",s);if(!a.getLanguage(s))throw u("Unknown language: `%s` is not registered",s);if("string"!=typeof o)throw u("Expected `string` for value, got `%s`",o);if(null==C&&(C=_),a.configure({__emitter:Emitter,classPrefix:C}),w=a.highlight(o,{language:s,ignoreIllegals:!0}),a.configure(x||{}),w.errorRaised)throw w.errorRaised;return{relevance:w.relevance,language:w.language,value:w.emitter.rootNode.children}}function Emitter(s){this.options=s,this.rootNode={children:[]},this.stack=[this.rootNode]}function noop(){}},71514(s){"use strict";s.exports=Math.abs},58968(s){"use strict";s.exports=Math.floor},94459(s){"use strict";s.exports=Number.isNaN||function isNaN(s){return s!=s}},6188(s){"use strict";s.exports=Math.max},68002(s){"use strict";s.exports=Math.min},75880(s){"use strict";s.exports=Math.pow},70414(s){"use strict";s.exports=Math.round},73093(s,o,i){"use strict";var a=i(94459);s.exports=function sign(s){return a(s)||0===s?s:s<0?-1:1}},92340(s,o,i){const a=i(6048);function coerceElementMatchingCallback(s){return"string"==typeof s?o=>o.element===s:s.constructor&&s.extend?o=>o instanceof s:s}class ArraySlice{constructor(s){this.elements=s||[]}toValue(){return this.elements.map((s=>s.toValue()))}map(s,o){return this.elements.map(s,o)}flatMap(s,o){return this.map(s,o).reduce(((s,o)=>s.concat(o)),[])}compactMap(s,o){const i=[];return this.forEach((a=>{const u=s.bind(o)(a);u&&i.push(u)})),i}filter(s,o){return s=coerceElementMatchingCallback(s),new ArraySlice(this.elements.filter(s,o))}reject(s,o){return s=coerceElementMatchingCallback(s),new ArraySlice(this.elements.filter(a(s),o))}find(s,o){return s=coerceElementMatchingCallback(s),this.elements.find(s,o)}forEach(s,o){this.elements.forEach(s,o)}reduce(s,o){return this.elements.reduce(s,o)}includes(s){return this.elements.some((o=>o.equals(s)))}shift(){return this.elements.shift()}unshift(s){this.elements.unshift(this.refract(s))}push(s){return this.elements.push(this.refract(s)),this}add(s){this.push(s)}get(s){return this.elements[s]}getValue(s){const o=this.elements[s];if(o)return o.toValue()}get length(){return this.elements.length}get isEmpty(){return 0===this.elements.length}get first(){return this.elements[0]}}"undefined"!=typeof Symbol&&(ArraySlice.prototype[Symbol.iterator]=function symbol(){return this.elements[Symbol.iterator]()}),s.exports=ArraySlice},55973(s){class KeyValuePair{constructor(s,o){this.key=s,this.value=o}clone(){const s=new KeyValuePair;return this.key&&(s.key=this.key.clone()),this.value&&(s.value=this.value.clone()),s}}s.exports=KeyValuePair},3110(s,o,i){const a=i(5187),u=i(85015),_=i(98023),w=i(53812),x=i(23805),C=i(85105),j=i(86804);class Namespace{constructor(s){this.elementMap={},this.elementDetection=[],this.Element=j.Element,this.KeyValuePair=j.KeyValuePair,s&&s.noDefault||this.useDefault(),this._attributeElementKeys=[],this._attributeElementArrayKeys=[]}use(s){return s.namespace&&s.namespace({base:this}),s.load&&s.load({base:this}),this}useDefault(){return this.register("null",j.NullElement).register("string",j.StringElement).register("number",j.NumberElement).register("boolean",j.BooleanElement).register("array",j.ArrayElement).register("object",j.ObjectElement).register("member",j.MemberElement).register("ref",j.RefElement).register("link",j.LinkElement),this.detect(a,j.NullElement,!1).detect(u,j.StringElement,!1).detect(_,j.NumberElement,!1).detect(w,j.BooleanElement,!1).detect(Array.isArray,j.ArrayElement,!1).detect(x,j.ObjectElement,!1),this}register(s,o){return this._elements=void 0,this.elementMap[s]=o,this}unregister(s){return this._elements=void 0,delete this.elementMap[s],this}detect(s,o,i){return void 0===i||i?this.elementDetection.unshift([s,o]):this.elementDetection.push([s,o]),this}toElement(s){if(s instanceof this.Element)return s;let o;for(let i=0;i{const o=s[0].toUpperCase()+s.substr(1);this._elements[o]=this.elementMap[s]}))),this._elements}get serialiser(){return new C(this)}}C.prototype.Namespace=Namespace,s.exports=Namespace},10866(s,o,i){const a=i(6048),u=i(92340);class ObjectSlice extends u{map(s,o){return this.elements.map((i=>s.bind(o)(i.value,i.key,i)))}filter(s,o){return new ObjectSlice(this.elements.filter((i=>s.bind(o)(i.value,i.key,i))))}reject(s,o){return this.filter(a(s.bind(o)))}forEach(s,o){return this.elements.forEach(((i,a)=>{s.bind(o)(i.value,i.key,i,a)}))}keys(){return this.map(((s,o)=>o.toValue()))}values(){return this.map((s=>s.toValue()))}}s.exports=ObjectSlice},86804(s,o,i){const a=i(10316),u=i(41067),_=i(71167),w=i(40239),x=i(12242),C=i(6233),j=i(87726),L=i(61045),B=i(86303),$=i(14540),z=i(92340),U=i(10866),V=i(55973);function refract(s){if(s instanceof a)return s;if("string"==typeof s)return new _(s);if("number"==typeof s)return new w(s);if("boolean"==typeof s)return new x(s);if(null===s)return new u;if(Array.isArray(s))return new C(s.map(refract));if("object"==typeof s){return new L(s)}return s}a.prototype.ObjectElement=L,a.prototype.RefElement=$,a.prototype.MemberElement=j,a.prototype.refract=refract,z.prototype.refract=refract,s.exports={Element:a,NullElement:u,StringElement:_,NumberElement:w,BooleanElement:x,ArrayElement:C,MemberElement:j,ObjectElement:L,LinkElement:B,RefElement:$,refract,ArraySlice:z,ObjectSlice:U,KeyValuePair:V}},86303(s,o,i){const a=i(10316);s.exports=class LinkElement extends a{constructor(s,o,i){super(s||[],o,i),this.element="link"}get relation(){return this.attributes.get("relation")}set relation(s){this.attributes.set("relation",s)}get href(){return this.attributes.get("href")}set href(s){this.attributes.set("href",s)}}},14540(s,o,i){const a=i(10316);s.exports=class RefElement extends a{constructor(s,o,i){super(s||[],o,i),this.element="ref",this.path||(this.path="element")}get path(){return this.attributes.get("path")}set path(s){this.attributes.set("path",s)}}},34035(s,o,i){const a=i(3110),u=i(86804);o.g$=a,o.KeyValuePair=i(55973),o.G6=u.ArraySlice,o.ot=u.ObjectSlice,o.Hg=u.Element,o.Om=u.StringElement,o.kT=u.NumberElement,o.bd=u.BooleanElement,o.Os=u.NullElement,o.wE=u.ArrayElement,o.Sh=u.ObjectElement,o.Pr=u.MemberElement,o.sI=u.RefElement,o.Ft=u.LinkElement,o.e=u.refract,i(85105),i(75147)},6233(s,o,i){const a=i(6048),u=i(10316),_=i(92340);class ArrayElement extends u{constructor(s,o,i){super(s||[],o,i),this.element="array"}primitive(){return"array"}get(s){return this.content[s]}getValue(s){const o=this.get(s);if(o)return o.toValue()}getIndex(s){return this.content[s]}set(s,o){return this.content[s]=this.refract(o),this}remove(s){const o=this.content.splice(s,1);return o.length?o[0]:null}map(s,o){return this.content.map(s,o)}flatMap(s,o){return this.map(s,o).reduce(((s,o)=>s.concat(o)),[])}compactMap(s,o){const i=[];return this.forEach((a=>{const u=s.bind(o)(a);u&&i.push(u)})),i}filter(s,o){return new _(this.content.filter(s,o))}reject(s,o){return this.filter(a(s),o)}reduce(s,o){let i,a;void 0!==o?(i=0,a=this.refract(o)):(i=1,a="object"===this.primitive()?this.first.value:this.first);for(let o=i;o{s.bind(o)(i,this.refract(a))}))}shift(){return this.content.shift()}unshift(s){this.content.unshift(this.refract(s))}push(s){return this.content.push(this.refract(s)),this}add(s){this.push(s)}findElements(s,o){const i=o||{},a=!!i.recursive,u=void 0===i.results?[]:i.results;return this.forEach(((o,i,_)=>{a&&void 0!==o.findElements&&o.findElements(s,{results:u,recursive:a}),s(o,i,_)&&u.push(o)})),u}find(s){return new _(this.findElements(s,{recursive:!0}))}findByElement(s){return this.find((o=>o.element===s))}findByClass(s){return this.find((o=>o.classes.includes(s)))}getById(s){return this.find((o=>o.id.toValue()===s)).first}includes(s){return this.content.some((o=>o.equals(s)))}contains(s){return this.includes(s)}empty(){return new this.constructor([])}"fantasy-land/empty"(){return this.empty()}concat(s){return new this.constructor(this.content.concat(s.content))}"fantasy-land/concat"(s){return this.concat(s)}"fantasy-land/map"(s){return new this.constructor(this.map(s))}"fantasy-land/chain"(s){return this.map((o=>s(o)),this).reduce(((s,o)=>s.concat(o)),this.empty())}"fantasy-land/filter"(s){return new this.constructor(this.content.filter(s))}"fantasy-land/reduce"(s,o){return this.content.reduce(s,o)}get length(){return this.content.length}get isEmpty(){return 0===this.content.length}get first(){return this.getIndex(0)}get second(){return this.getIndex(1)}get last(){return this.getIndex(this.length-1)}}ArrayElement.empty=function empty(){return new this},ArrayElement["fantasy-land/empty"]=ArrayElement.empty,"undefined"!=typeof Symbol&&(ArrayElement.prototype[Symbol.iterator]=function symbol(){return this.content[Symbol.iterator]()}),s.exports=ArrayElement},12242(s,o,i){const a=i(10316);s.exports=class BooleanElement extends a{constructor(s,o,i){super(s,o,i),this.element="boolean"}primitive(){return"boolean"}}},10316(s,o,i){const a=i(2404),u=i(55973),_=i(92340);class Element{constructor(s,o,i){o&&(this.meta=o),i&&(this.attributes=i),this.content=s}freeze(){Object.isFrozen(this)||(this._meta&&(this.meta.parent=this,this.meta.freeze()),this._attributes&&(this.attributes.parent=this,this.attributes.freeze()),this.children.forEach((s=>{s.parent=this,s.freeze()}),this),this.content&&Array.isArray(this.content)&&Object.freeze(this.content),Object.freeze(this))}primitive(){}clone(){const s=new this.constructor;return s.element=this.element,this.meta.length&&(s._meta=this.meta.clone()),this.attributes.length&&(s._attributes=this.attributes.clone()),this.content?this.content.clone?s.content=this.content.clone():Array.isArray(this.content)?s.content=this.content.map((s=>s.clone())):s.content=this.content:s.content=this.content,s}toValue(){return this.content instanceof Element?this.content.toValue():this.content instanceof u?{key:this.content.key.toValue(),value:this.content.value?this.content.value.toValue():void 0}:this.content&&this.content.map?this.content.map((s=>s.toValue()),this):this.content}toRef(s){if(""===this.id.toValue())throw Error("Cannot create reference to an element that does not contain an ID");const o=new this.RefElement(this.id.toValue());return s&&(o.path=s),o}findRecursive(...s){if(arguments.length>1&&!this.isFrozen)throw new Error("Cannot find recursive with multiple element names without first freezing the element. Call `element.freeze()`");const o=s.pop();let i=new _;const append=(s,o)=>(s.push(o),s),checkElement=(s,i)=>{i.element===o&&s.push(i);const a=i.findRecursive(o);return a&&a.reduce(append,s),i.content instanceof u&&(i.content.key&&checkElement(s,i.content.key),i.content.value&&checkElement(s,i.content.value)),s};return this.content&&(this.content.element&&checkElement(i,this.content),Array.isArray(this.content)&&this.content.reduce(checkElement,i)),s.isEmpty||(i=i.filter((o=>{let i=o.parents.map((s=>s.element));for(const o in s){const a=s[o],u=i.indexOf(a);if(-1===u)return!1;i=i.splice(0,u)}return!0}))),i}set(s){return this.content=s,this}equals(s){return a(this.toValue(),s)}getMetaProperty(s,o){if(!this.meta.hasKey(s)){if(this.isFrozen){const s=this.refract(o);return s.freeze(),s}this.meta.set(s,o)}return this.meta.get(s)}setMetaProperty(s,o){this.meta.set(s,o)}get element(){return this._storedElement||"element"}set element(s){this._storedElement=s}get content(){return this._content}set content(s){if(s instanceof Element)this._content=s;else if(s instanceof _)this.content=s.elements;else if("string"==typeof s||"number"==typeof s||"boolean"==typeof s||"null"===s||null==s)this._content=s;else if(s instanceof u)this._content=s;else if(Array.isArray(s))this._content=s.map(this.refract);else{if("object"!=typeof s)throw new Error("Cannot set content to given value");this._content=Object.keys(s).map((o=>new this.MemberElement(o,s[o])))}}get meta(){if(!this._meta){if(this.isFrozen){const s=new this.ObjectElement;return s.freeze(),s}this._meta=new this.ObjectElement}return this._meta}set meta(s){s instanceof this.ObjectElement?this._meta=s:this.meta.set(s||{})}get attributes(){if(!this._attributes){if(this.isFrozen){const s=new this.ObjectElement;return s.freeze(),s}this._attributes=new this.ObjectElement}return this._attributes}set attributes(s){s instanceof this.ObjectElement?this._attributes=s:this.attributes.set(s||{})}get id(){return this.getMetaProperty("id","")}set id(s){this.setMetaProperty("id",s)}get classes(){return this.getMetaProperty("classes",[])}set classes(s){this.setMetaProperty("classes",s)}get title(){return this.getMetaProperty("title","")}set title(s){this.setMetaProperty("title",s)}get description(){return this.getMetaProperty("description","")}set description(s){this.setMetaProperty("description",s)}get links(){return this.getMetaProperty("links",[])}set links(s){this.setMetaProperty("links",s)}get isFrozen(){return Object.isFrozen(this)}get parents(){let{parent:s}=this;const o=new _;for(;s;)o.push(s),s=s.parent;return o}get children(){if(Array.isArray(this.content))return new _(this.content);if(this.content instanceof u){const s=new _([this.content.key]);return this.content.value&&s.push(this.content.value),s}return this.content instanceof Element?new _([this.content]):new _}get recursiveChildren(){const s=new _;return this.children.forEach((o=>{s.push(o),o.recursiveChildren.forEach((o=>{s.push(o)}))})),s}}s.exports=Element},87726(s,o,i){const a=i(55973),u=i(10316);s.exports=class MemberElement extends u{constructor(s,o,i,u){super(new a,i,u),this.element="member",this.key=s,this.value=o}get key(){return this.content.key}set key(s){this.content.key=this.refract(s)}get value(){return this.content.value}set value(s){this.content.value=this.refract(s)}}},41067(s,o,i){const a=i(10316);s.exports=class NullElement extends a{constructor(s,o,i){super(s||null,o,i),this.element="null"}primitive(){return"null"}set(){return new Error("Cannot set the value of null")}}},40239(s,o,i){const a=i(10316);s.exports=class NumberElement extends a{constructor(s,o,i){super(s,o,i),this.element="number"}primitive(){return"number"}}},61045(s,o,i){const a=i(6048),u=i(23805),_=i(6233),w=i(87726),x=i(10866);s.exports=class ObjectElement extends _{constructor(s,o,i){super(s||[],o,i),this.element="object"}primitive(){return"object"}toValue(){return this.content.reduce(((s,o)=>(s[o.key.toValue()]=o.value?o.value.toValue():void 0,s)),{})}get(s){const o=this.getMember(s);if(o)return o.value}getMember(s){if(void 0!==s)return this.content.find((o=>o.key.toValue()===s))}remove(s){let o=null;return this.content=this.content.filter((i=>i.key.toValue()!==s||(o=i,!1))),o}getKey(s){const o=this.getMember(s);if(o)return o.key}set(s,o){if(u(s))return Object.keys(s).forEach((o=>{this.set(o,s[o])})),this;const i=s,a=this.getMember(i);return a?a.value=o:this.content.push(new w(i,o)),this}keys(){return this.content.map((s=>s.key.toValue()))}values(){return this.content.map((s=>s.value.toValue()))}hasKey(s){return this.content.some((o=>o.key.equals(s)))}items(){return this.content.map((s=>[s.key.toValue(),s.value.toValue()]))}map(s,o){return this.content.map((i=>s.bind(o)(i.value,i.key,i)))}compactMap(s,o){const i=[];return this.forEach(((a,u,_)=>{const w=s.bind(o)(a,u,_);w&&i.push(w)})),i}filter(s,o){return new x(this.content).filter(s,o)}reject(s,o){return this.filter(a(s),o)}forEach(s,o){return this.content.forEach((i=>s.bind(o)(i.value,i.key,i)))}}},71167(s,o,i){const a=i(10316);s.exports=class StringElement extends a{constructor(s,o,i){super(s,o,i),this.element="string"}primitive(){return"string"}get length(){return this.content.length}}},75147(s,o,i){const a=i(85105);s.exports=class JSON06Serialiser extends a{serialise(s){if(!(s instanceof this.namespace.elements.Element))throw new TypeError(`Given element \`${s}\` is not an Element instance`);let o;s._attributes&&s.attributes.get("variable")&&(o=s.attributes.get("variable"));const i={element:s.element};s._meta&&s._meta.length>0&&(i.meta=this.serialiseObject(s.meta));const a="enum"===s.element||-1!==s.attributes.keys().indexOf("enumerations");if(a){const o=this.enumSerialiseAttributes(s);o&&(i.attributes=o)}else if(s._attributes&&s._attributes.length>0){let{attributes:a}=s;a.get("metadata")&&(a=a.clone(),a.set("meta",a.get("metadata")),a.remove("metadata")),"member"===s.element&&o&&(a=a.clone(),a.remove("variable")),a.length>0&&(i.attributes=this.serialiseObject(a))}if(a)i.content=this.enumSerialiseContent(s,i);else if(this[`${s.element}SerialiseContent`])i.content=this[`${s.element}SerialiseContent`](s,i);else if(void 0!==s.content){let a;o&&s.content.key?(a=s.content.clone(),a.key.attributes.set("variable",o),a=this.serialiseContent(a)):a=this.serialiseContent(s.content),this.shouldSerialiseContent(s,a)&&(i.content=a)}else this.shouldSerialiseContent(s,s.content)&&s instanceof this.namespace.elements.Array&&(i.content=[]);return i}shouldSerialiseContent(s,o){return"parseResult"===s.element||"httpRequest"===s.element||"httpResponse"===s.element||"category"===s.element||"link"===s.element||void 0!==o&&(!Array.isArray(o)||0!==o.length)}refSerialiseContent(s,o){return delete o.attributes,{href:s.toValue(),path:s.path.toValue()}}sourceMapSerialiseContent(s){return s.toValue()}dataStructureSerialiseContent(s){return[this.serialiseContent(s.content)]}enumSerialiseAttributes(s){const o=s.attributes.clone(),i=o.remove("enumerations")||new this.namespace.elements.Array([]),a=o.get("default");let u=o.get("samples")||new this.namespace.elements.Array([]);if(a&&a.content&&(a.content.attributes&&a.content.attributes.remove("typeAttributes"),o.set("default",new this.namespace.elements.Array([a.content]))),u.forEach((s=>{s.content&&s.content.element&&s.content.attributes.remove("typeAttributes")})),s.content&&0!==i.length&&u.unshift(s.content),u=u.map((s=>s instanceof this.namespace.elements.Array?[s]:new this.namespace.elements.Array([s.content]))),u.length&&o.set("samples",u),o.length>0)return this.serialiseObject(o)}enumSerialiseContent(s){if(s._attributes){const o=s.attributes.get("enumerations");if(o&&o.length>0)return o.content.map((s=>{const o=s.clone();return o.attributes.remove("typeAttributes"),this.serialise(o)}))}if(s.content){const o=s.content.clone();return o.attributes.remove("typeAttributes"),[this.serialise(o)]}return[]}deserialise(s){if("string"==typeof s)return new this.namespace.elements.String(s);if("number"==typeof s)return new this.namespace.elements.Number(s);if("boolean"==typeof s)return new this.namespace.elements.Boolean(s);if(null===s)return new this.namespace.elements.Null;if(Array.isArray(s))return new this.namespace.elements.Array(s.map(this.deserialise,this));const o=this.namespace.getElementClass(s.element),i=new o;i.element!==s.element&&(i.element=s.element),s.meta&&this.deserialiseObject(s.meta,i.meta),s.attributes&&this.deserialiseObject(s.attributes,i.attributes);const a=this.deserialiseContent(s.content);if(void 0===a&&null!==i.content||(i.content=a),"enum"===i.element){i.content&&i.attributes.set("enumerations",i.content);let s=i.attributes.get("samples");if(i.attributes.remove("samples"),s){const a=s;s=new this.namespace.elements.Array,a.forEach((a=>{a.forEach((a=>{const u=new o(a);u.element=i.element,s.push(u)}))}));const u=s.shift();i.content=u?u.content:void 0,i.attributes.set("samples",s)}else i.content=void 0;let a=i.attributes.get("default");if(a&&a.length>0){a=a.get(0);const s=new o(a);s.element=i.element,i.attributes.set("default",s)}}else if("dataStructure"===i.element&&Array.isArray(i.content))[i.content]=i.content;else if("category"===i.element){const s=i.attributes.get("meta");s&&(i.attributes.set("metadata",s),i.attributes.remove("meta"))}else"member"===i.element&&i.key&&i.key._attributes&&i.key._attributes.getValue("variable")&&(i.attributes.set("variable",i.key.attributes.get("variable")),i.key.attributes.remove("variable"));return i}serialiseContent(s){if(s instanceof this.namespace.elements.Element)return this.serialise(s);if(s instanceof this.namespace.KeyValuePair){const o={key:this.serialise(s.key)};return s.value&&(o.value=this.serialise(s.value)),o}return s&&s.map?s.map(this.serialise,this):s}deserialiseContent(s){if(s){if(s.element)return this.deserialise(s);if(s.key){const o=new this.namespace.KeyValuePair(this.deserialise(s.key));return s.value&&(o.value=this.deserialise(s.value)),o}if(s.map)return s.map(this.deserialise,this)}return s}shouldRefract(s){return!!(s._attributes&&s.attributes.keys().length||s._meta&&s.meta.keys().length)||"enum"!==s.element&&(s.element!==s.primitive()||"member"===s.element)}convertKeyToRefract(s,o){return this.shouldRefract(o)?this.serialise(o):"enum"===o.element?this.serialiseEnum(o):"array"===o.element?o.map((o=>this.shouldRefract(o)||"default"===s?this.serialise(o):"array"===o.element||"object"===o.element||"enum"===o.element?o.children.map((s=>this.serialise(s))):o.toValue())):"object"===o.element?(o.content||[]).map(this.serialise,this):o.toValue()}serialiseEnum(s){return s.children.map((s=>this.serialise(s)))}serialiseObject(s){const o={};return s.forEach(((s,i)=>{if(s){const a=i.toValue();o[a]=this.convertKeyToRefract(a,s)}})),o}deserialiseObject(s,o){Object.keys(s).forEach((i=>{o.set(i,this.deserialise(s[i]))}))}}},85105(s){s.exports=class JSONSerialiser{constructor(s){this.namespace=s||new this.Namespace}serialise(s){if(!(s instanceof this.namespace.elements.Element))throw new TypeError(`Given element \`${s}\` is not an Element instance`);const o={element:s.element};s._meta&&s._meta.length>0&&(o.meta=this.serialiseObject(s.meta)),s._attributes&&s._attributes.length>0&&(o.attributes=this.serialiseObject(s.attributes));const i=this.serialiseContent(s.content);return void 0!==i&&(o.content=i),o}deserialise(s){if(!s.element)throw new Error("Given value is not an object containing an element name");const o=new(this.namespace.getElementClass(s.element));o.element!==s.element&&(o.element=s.element),s.meta&&this.deserialiseObject(s.meta,o.meta),s.attributes&&this.deserialiseObject(s.attributes,o.attributes);const i=this.deserialiseContent(s.content);return void 0===i&&null!==o.content||(o.content=i),o}serialiseContent(s){if(s instanceof this.namespace.elements.Element)return this.serialise(s);if(s instanceof this.namespace.KeyValuePair){const o={key:this.serialise(s.key)};return s.value&&(o.value=this.serialise(s.value)),o}if(s&&s.map){if(0===s.length)return;return s.map(this.serialise,this)}return s}deserialiseContent(s){if(s){if(s.element)return this.deserialise(s);if(s.key){const o=new this.namespace.KeyValuePair(this.deserialise(s.key));return s.value&&(o.value=this.deserialise(s.value)),o}if(s.map)return s.map(this.deserialise,this)}return s}serialiseObject(s){const o={};if(s.forEach(((s,i)=>{s&&(o[i.toValue()]=this.serialise(s))})),0!==Object.keys(o).length)return o}deserialiseObject(s,o){Object.keys(s).forEach((i=>{o.set(i,this.deserialise(s[i]))}))}}},76578(s){"use strict";s.exports=["Float16Array","Float32Array","Float64Array","Int8Array","Int16Array","Int32Array","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array"]},65606(s){var o,i,a=s.exports={};function defaultSetTimout(){throw new Error("setTimeout has not been defined")}function defaultClearTimeout(){throw new Error("clearTimeout has not been defined")}function runTimeout(s){if(o===setTimeout)return setTimeout(s,0);if((o===defaultSetTimout||!o)&&setTimeout)return o=setTimeout,setTimeout(s,0);try{return o(s,0)}catch(i){try{return o.call(null,s,0)}catch(i){return o.call(this,s,0)}}}!function(){try{o="function"==typeof setTimeout?setTimeout:defaultSetTimout}catch(s){o=defaultSetTimout}try{i="function"==typeof clearTimeout?clearTimeout:defaultClearTimeout}catch(s){i=defaultClearTimeout}}();var u,_=[],w=!1,x=-1;function cleanUpNextTick(){w&&u&&(w=!1,u.length?_=u.concat(_):x=-1,_.length&&drainQueue())}function drainQueue(){if(!w){var s=runTimeout(cleanUpNextTick);w=!0;for(var o=_.length;o;){for(u=_,_=[];++x1)for(var i=1;i_)throw new RangeError("requested too many random bytes");var i=w.allocUnsafe(s);if(s>0)if(s>u)for(var C=0;C=0||(u[i]=s[i]);return u}(s,o);if(Object.getOwnPropertySymbols){var _=Object.getOwnPropertySymbols(s);for(a=0;a<_.length;a++)i=_[a],o.indexOf(i)>=0||Object.prototype.propertyIsEnumerable.call(s,i)&&(u[i]=s[i])}return u}function ownKeys(s,o){var i=Object.keys(s);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(s);o&&(a=a.filter((function(o){return Object.getOwnPropertyDescriptor(s,o).enumerable}))),i.push.apply(i,a)}return i}function _objectSpread(s){for(var o=1;o=a?i.notify(s):o.length>u.length&&i.notify(_objectSpread(_objectSpread({},s),{},{target:_objectSpread(_objectSpread({},s.target),{},{value:""})}))}))})),_defineProperty(_assertThisInitialized(i),"onKeyDown",(function(s){"Enter"===s.key&&i.forceNotify(s);var o=i.props.onKeyDown;o&&(s.persist(),o(s))})),_defineProperty(_assertThisInitialized(i),"onBlur",(function(s){i.forceNotify(s);var o=i.props.onBlur;o&&(s.persist(),o(s))})),_defineProperty(_assertThisInitialized(i),"createNotifier",(function(s){if(s<0)i.notify=function(){return null};else if(0===s)i.notify=i.doNotify;else{var o=(0,u.default)((function(s){i.isDebouncing=!1,i.doNotify(s)}),s);i.notify=function(s){i.isDebouncing=!0,o(s)},i.flush=function(){return o.flush()},i.cancel=function(){i.isDebouncing=!1,o.cancel()}}})),_defineProperty(_assertThisInitialized(i),"doNotify",(function(){i.props.onChange.apply(void 0,arguments)})),_defineProperty(_assertThisInitialized(i),"forceNotify",(function(s){var o=i.props.debounceTimeout;if(i.isDebouncing||!(o>0)){i.cancel&&i.cancel();var a=i.state.value,u=i.props.minLength;a.length>=u?i.doNotify(s):i.doNotify(_objectSpread(_objectSpread({},s),{},{target:_objectSpread(_objectSpread({},s.target),{},{value:a})}))}})),i.isDebouncing=!1,i.state={value:void 0===s.value||null===s.value?"":s.value};var a=i.props.debounceTimeout;return i.createNotifier(a),i}return function _createClass(s,o,i){return o&&_defineProperties(s.prototype,o),i&&_defineProperties(s,i),Object.defineProperty(s,"prototype",{writable:!1}),s}(DebounceInput,[{key:"componentDidUpdate",value:function componentDidUpdate(s){if(!this.isDebouncing){var o=this.props,i=o.value,a=o.debounceTimeout,u=s.debounceTimeout,_=s.value,w=this.state.value;void 0!==i&&_!==i&&w!==i&&this.setState({value:i}),a!==u&&this.createNotifier(a)}}},{key:"componentWillUnmount",value:function componentWillUnmount(){this.flush&&this.flush()}},{key:"render",value:function render(){var s,o,i=this.props,u=i.element,w=(i.onChange,i.value,i.minLength,i.debounceTimeout,i.forceNotifyByEnter),x=i.forceNotifyOnBlur,C=i.onKeyDown,j=i.onBlur,L=i.inputRef,B=_objectWithoutProperties(i,_),$=this.state.value;s=w?{onKeyDown:this.onKeyDown}:C?{onKeyDown:C}:{},o=x?{onBlur:this.onBlur}:j?{onBlur:j}:{};var z=L?{ref:L}:{};return a.default.createElement(u,_objectSpread(_objectSpread(_objectSpread(_objectSpread({},B),{},{onChange:this.onChange,value:$},s),o),z))}}]),DebounceInput}(a.default.PureComponent);o.DebounceInput=w,_defineProperty(w,"defaultProps",{element:"input",type:"text",onKeyDown:void 0,onBlur:void 0,value:void 0,minLength:0,debounceTimeout:100,forceNotifyByEnter:!0,forceNotifyOnBlur:!0,inputRef:void 0})},24677(s,o,i){"use strict";var a=i(81214).DebounceInput;a.DebounceInput=a,s.exports=a},22551(s,o,i){"use strict";var a=i(96540),u=i(69982);function p(s){for(var o="https://reactjs.org/docs/error-decoder.html?invariant="+s,i=1;i