diff --git a/docs/api-inventory.json b/docs/api-inventory.json new file mode 100644 index 00000000..53b0d85a --- /dev/null +++ b/docs/api-inventory.json @@ -0,0 +1,4787 @@ +[ + { + "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\"" + }, + { + "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 '{}'" + } + ] + }, + { + "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\"" + }, + { + "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\"" + } + ] + }, + { + "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\"" + }, + { + "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 '{}'" + } + ] + }, + { + "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": "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\"" + } + ] + }, + { + "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 '{}'" + }, + { + "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 '{}'" + }, + { + "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\"" + }, + { + "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\"" + }, + { + "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 '{}'" + }, + { + "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 '{}'" + } + ] + }, + { + "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 '{}'" + }, + { + "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\"" + } + ] + }, + { + "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\"" + }, + { + "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 '{}'" + }, + { + "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 '{}'" + }, + { + "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 '{}'" + } + ] + }, + { + "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\"" + }, + { + "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\"" + } + ] + }, + { + "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" + } + ] + }, + { + "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\"" + }, + { + "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 '{}'" + }, + { + "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 '{}'" + }, + { + "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 '{}'" + } + ] + }, + { + "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\"" + }, + { + "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 '{}'" + } + ] + }, + { + "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 '{}'" + }, + { + "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\"" + }, + { + "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 '{}'" + }, + { + "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 '{}'" + }, + { + "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 '{}'" + } + ] + }, + { + "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\"" + }, + { + "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" + } + ] + }, + { + "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\"" + }, + { + "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 '{}'" + }, + { + "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 '{}'" + } + ] + }, + { + "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 '{}'" + } + ] + }, + { + "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 '{}'" + }, + { + "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\"" + } + ] + }, + { + "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\"" + }, + { + "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\"" + } + ] + }, + { + "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\"" + }, + { + "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\"" + } + ] + }, + { + "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\"" + }, + { + "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\"" + } + ] + }, + { + "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\"" + }, + { + "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\"" + } + ] + }, + { + "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\"" + }, + { + "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 '{}'" + } + ] + }, + { + "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 '{}'" + }, + { + "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 '{}'" + } + ] + }, + { + "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\"" + }, + { + "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\"" + } + ] + }, + { + "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\"" + }, + { + "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 '{}'" + } + ] + }, + { + "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\"" + }, + { + "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 '{}'" + }, + { + "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\"" + }, + { + "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 '{}'" + } + ] + }, + { + "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\"" + }, + { + "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 '{}'" + } + ] + }, + { + "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\"" + }, + { + "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\"" + } + ] + }, + { + "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\"" + }, + { + "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 '{}'" + } + ] + }, + { + "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\"" + }, + { + "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\"" + } + ] + }, + { + "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 '{}'" + } + ] + }, + { + "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\"" + }, + { + "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\"" + } + ] + }, + { + "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\"" + }, + { + "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 '{}'" + }, + { + "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 '{}'" + }, + { + "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\"" + } + ] + }, + { + "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\"" + }, + { + "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\"" + } + ] + }, + { + "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\"" + }, + { + "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 '{}'" + } + ] + }, + { + "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\"" + }, + { + "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 '{}'" + } + ] + }, + { + "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\"" + }, + { + "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 '{}'" + } + ] + }, + { + "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": "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 '{}'" + }, + { + "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 '{}'" + } + ] + }, + { + "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\"" + } + ] + }, + { + "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\"" + }, + { + "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 '{}'" + } + ] + }, + { + "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\"" + }, + { + "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" + } + ] + }, + { + "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 '{}'" + }, + { + "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" + }, + { + "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 '{}'" + } + ] + }, + { + "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\"" + }, + { + "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 '{}'" + }, + { + "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 '{}'" + }, + { + "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\"" + } + ] + }, + { + "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\"" + }, + { + "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 '{}'" + } + ] + } +] diff --git a/docs/openapi.json b/docs/openapi.json index 683ad40b..0dd38f91 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -3,7 +3,7 @@ "info": { "title": "ShopDB Flask API", "version": "0.12.0", - "description": "Asset-management API (core + plugins). Responses use a `success_response` envelope: `{status, data, meta}`. Auth: Bearer JWT (login or a managed PAT) for `jwt`/`admin`/`permission:*`; `X-API-Key` for collector/managed-token endpoints; public endpoints need neither." + "description": "Asset-management API (core + plugins). Responses use a `success_response` envelope: `{status, data, meta}`. Auth: Bearer JWT (login or a managed PAT) for `jwt`/`admin`/`permission:*`; `X-API-Key` for collector/managed-token endpoints; public endpoints need neither. A permission-gated route is published as bearer-required, which is what a default install does - a site that grants the permission to the `guest` role makes that same route answer a caller with no token." }, "servers": [ { @@ -115,809 +115,161 @@ "type": "apiKey", "in": "header", "name": "X-API-Key", - "description": "Managed service token (e.g. collector.ingest, geenforce.fetch)." + "description": "A managed service or device token in a header. X-API-Key for collector and managed tokens (collector.ingest, geenforce.fetch); X-Endpoint-Bootstrap-Token for agent bootstrap; X-Kiosk-Token for a kiosk screen. The operation description names the header and, where the gate is token-OR-network, the allowlist that is the other half." + }, + "setupCodeAuth": { + "type": "apiKey", + "in": "query", + "name": "code", + "description": "A single-use provisioning code, redeemed in the query string because the caller is a browser being pointed at a URL. Spent on any attempt, right or wrong, and short-lived." } } }, "tags": [ { - "name": "core-identity" + "name": "core-apitokens" }, { - "name": "core-platform" + "name": "core-applications" }, { - "name": "plugin-employees" + "name": "core-assets" }, { - "name": "plugin-knowledgebase" + "name": "core-auditlogs" }, { - "name": "core-catalog" + "name": "core-auth" }, { - "name": "plugin-computers" + "name": "core-businessunits" }, { - "name": "plugin-measuringtools" + "name": "core-collector" }, { - "name": "plugin-machines" + "name": "core-customfields" }, { - "name": "plugin-geenforce" + "name": "core-dashboard" }, { - "name": "plugin-notifications" - }, - { - "name": "plugin-slides" - }, - { - "name": "plugin-printedparts" - }, - { - "name": "plugin-network" - }, - { - "name": "plugin-usb" - }, - { - "name": "plugin-printers" - }, - { - "name": "plugin-warranty" - }, - { - "name": "plugin-backups" - }, - { - "name": "plugin-tools" + "name": "core-dashboarddefaults" }, { "name": "core-docs" }, { - "name": "core-map" + "name": "core-locations" }, { - "name": "guides" + "name": "core-maplevels" }, { - "name": "plugin-endpoints" + "name": "core-mappositions" + }, + { + "name": "core-models" + }, + { + "name": "core-modeltypes" + }, + { + "name": "core-operatingsystems" + }, + { + "name": "core-plugins" + }, + { + "name": "core-pluginui" + }, + { + "name": "core-reports" }, { "name": "core-scripts" }, { - "name": "endpoints-delivery" + "name": "core-search" }, { - "name": "endpoints-siteagent" + "name": "core-settings" + }, + { + "name": "core-setup" }, { "name": "core-shopdbbackups" + }, + { + "name": "core-supportteams" + }, + { + "name": "core-users" + }, + { + "name": "core-vendors" + }, + { + "name": "plugin-backups" + }, + { + "name": "plugin-computers" + }, + { + "name": "plugin-employees" + }, + { + "name": "plugin-endpoints" + }, + { + "name": "plugin-geenforce" + }, + { + "name": "plugin-guides" + }, + { + "name": "plugin-knowledgebase" + }, + { + "name": "plugin-machines" + }, + { + "name": "plugin-measuringtools" + }, + { + "name": "plugin-network" + }, + { + "name": "plugin-notifications" + }, + { + "name": "plugin-printedparts" + }, + { + "name": "plugin-printers" + }, + { + "name": "plugin-slides" + }, + { + "name": "plugin-tools" + }, + { + "name": "plugin-usb" + }, + { + "name": "plugin-warranty" } ], "paths": { - "/api/auth/login": { - "post": { - "tags": [ - "core-identity" - ], - "summary": "Authenticate and issue JWT access+refresh tokens; per-IP fixed-window rate limit (429) and 5-strike/15-min account...", - "description": "Authenticate and issue JWT access+refresh tokens; per-IP fixed-window rate limit (429) and 5-strike/15-min account lockout\n\n**Auth:** none\n\n**Params:** body: username, password (both required)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/auth/login -H 'Content-Type: application/json' -d '{\"username\":\"admin\",\"password\":\"secret123\"}'\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: username, password (both required)" - } - } - } - } - } - }, - "/api/auth/refresh": { - "post": { - "tags": [ - "core-identity" - ], - "summary": "Exchange a refresh token for a new access token (rejects inactive/deleted users)", - "description": "Exchange a refresh token for a new access token (rejects inactive/deleted users)\n\n**Auth:** jwt (refresh token)\n\n**Params:** none; refresh token in Authorization header\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/auth/refresh -H \"Authorization: Bearer $REFRESH_TOKEN\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "none; refresh token in Authorization header" - } - } - } - } - } - }, - "/api/auth/me": { - "get": { - "tags": [ - "core-identity" - ], - "summary": "Return the authenticated user's profile, roles, permissions, mustchangepassword flag", - "description": "Return the authenticated user's profile, roles, permissions, mustchangepassword flag\n\n**Auth:** jwt\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/auth/me -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/auth/change-password": { - "post": { - "tags": [ - "core-identity" - ], - "summary": "Self-service password change; forced first-login change skips current_password; clears lockout state", - "description": "Self-service password change; forced first-login change skips current_password; clears lockout state\n\n**Auth:** jwt\n\n**Params:** body: new_password (min 8, required), current_password (required unless mustchangepassword is set)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/auth/change-password -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"current_password\":\"old\",\"new_password\":\"newpass123\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: new_password (min 8, required), current_password (required unless mustchangepassword is set)" - } - } - } - } - } - }, - "/api/auth/logout": { - "post": { - "tags": [ - "core-identity" - ], - "summary": "Logout stub for frontend token cleanup (no server-side blacklist yet)", - "description": "Logout stub for frontend token cleanup (no server-side blacklist yet)\n\n**Auth:** jwt\n\n**Params:** none\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/auth/logout -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "none" - } - } - } - } - } - }, - "/api/users": { - "get": { - "tags": [ - "core-identity" - ], - "summary": "List all users ordered by username", - "description": "List all users ordered by username\n\n**Auth:** jwt + require_role admin\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/users -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - }, - "post": { - "tags": [ - "core-identity" - ], - "summary": "Create a user, assign roles, audit-log, and best-effort send a welcome email with the temp password", - "description": "Create a user, assign roles, audit-log, and best-effort send a welcome email with the temp password\n\n**Auth:** jwt + require_role admin\n\n**Params:** body: username, email, password (required); firstname, lastname, isactive, roles[roleids], mustchangepassword (default true), sendwelcome (default true)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/users -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"username\":\"jdoe\",\"email\":\"jdoe@example.com\",\"password\":\"Temp1234\",\"roles\":[2]}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: username, email, password (required); firstname, lastname, isactive, roles[roleids], mustchangepassword (default true), sendwelcome (default true)" - } - } - } - } - } - }, - "/api/users/{userid}": { - "get": { - "tags": [ - "core-identity" - ], - "summary": "Get one user; non-admins may only fetch their own record (403 otherwise)", - "description": "Get one user; non-admins may only fetch their own record (403 otherwise)\n\n**Auth:** jwt (admin or self, checked inline)\n\n**Params:** path: userid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/users/7 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "userid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "core-identity" - ], - "summary": "Update a user; email uniqueness enforced; admins can also change active state, roles, and unlock the account; changes...", - "description": "Update a user; email uniqueness enforced; admins can also change active state, roles, and unlock the account; changes audit-logged\n\n**Auth:** jwt (admin or self; isactive/roles/unlock fields admin-only)\n\n**Params:** path: userid; body: email, firstname, lastname, password; admin-only: isactive, roles[roleids], unlock (bool)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/users/7 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"firstname\":\"Jane\",\"unlock\":true}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "userid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: userid; body: email, firstname, lastname, password; admin-only: isactive, roles[roleids], unlock (bool)" - } - } - } - } - }, - "delete": { - "tags": [ - "core-identity" - ], - "summary": "Delete a user; revokes their API tokens and detaches (nulls userid on) their audit-log rows first", - "description": "Delete a user; revokes their API tokens and detaches (nulls userid on) their audit-log rows first\n\n**Auth:** jwt + require_role admin\n\n**Params:** path: userid (cannot be your own account)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/users/7 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "userid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/users/permissions": { - "get": { - "tags": [ - "core-identity" - ], - "summary": "List assignable permissions (core + enabled plugins) both flat and grouped by category, for the role grid", - "description": "List assignable permissions (core + enabled plugins) both flat and grouped by category, for the role grid\n\n**Auth:** jwt\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/users/permissions -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/users/roles": { - "get": { - "tags": [ - "core-identity" - ], - "summary": "List all roles with description, color, user count, permission names, and isadmin flag", - "description": "List all roles with description, color, user count, permission names, and isadmin flag\n\n**Auth:** jwt\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/users/roles -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - }, - "post": { - "tags": [ - "core-identity" - ], - "summary": "Create a role and assign permissions by name; 409 if rolename exists", - "description": "Create a role and assign permissions by name; 409 if rolename exists\n\n**Auth:** jwt + require_role admin\n\n**Params:** body: rolename (required), description, color, permissions[names]\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/users/roles -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"rolename\":\"viewer\",\"permissions\":[\"assets.view\"]}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: rolename (required), description, color, permissions[names]" - } - } - } - } - } - }, - "/api/users/roles/{roleid}": { - "put": { - "tags": [ - "core-identity" - ], - "summary": "Update a role's description/color/permissions; admin role's permission set cannot be modified; audit-logged", - "description": "Update a role's description/color/permissions; admin role's permission set cannot be modified; audit-logged\n\n**Auth:** jwt + require_role admin\n\n**Params:** path: roleid; body: description, color, permissions[names] (permissions immutable on the admin role)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/users/roles/3 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"color\":\"#0057b8\",\"permissions\":[\"assets.view\",\"assets.edit\"]}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "roleid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: roleid; body: description, color, permissions[names] (permissions immutable on the admin role)" - } - } - } - } - }, - "delete": { - "tags": [ - "core-identity" - ], - "summary": "Delete a role; refuses for the admin role or any role still assigned to users", - "description": "Delete a role; refuses for the admin role or any role still assigned to users\n\n**Auth:** jwt + require_role admin\n\n**Params:** path: roleid\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/users/roles/3 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "roleid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, "/api/apitokens": { "get": { "tags": [ - "core-identity" + "core-apitokens" ], - "summary": "List the caller's own API tokens (never returns hashes or secrets); admins may list all", - "description": "List the caller's own API tokens (never returns hashes or secrets); admins may list all\n\n**Auth:** jwt\n\n**Params:** query: all=true (admin only, lists everyone's tokens with owner info)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/apitokens?all=true' -H \"Authorization: Bearer $TOK\"\n```", + "summary": "List the caller's own tokens. Admins may pass ?all=true for everyone's.", + "description": "List the caller's own tokens. Admins may pass ?all=true for everyone's.\n\n**Auth:** jwt\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/apitokens -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -950,10 +302,10 @@ }, "post": { "tags": [ - "core-identity" + "core-apitokens" ], - "summary": "Create a personal API token for the caller; the full secret is returned once in this response and never again", - "description": "Create a personal API token for the caller; the full secret is returned once in this response and never again\n\n**Auth:** jwt + require_permission apitokens.create\n\n**Params:** body: name (required), expiresat (date/datetime), scopes[permission names, ceiling = owner's permissions], resourcescopes[resource names, plugin-defined]\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/apitokens -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"name\":\"import-script\",\"scopes\":[\"assets.edit\"],\"expiresat\":\"2026-12-31\"}'\n```", + "summary": "Create a token for the caller. Returns the full secret ONCE.", + "description": "Create a token for the caller. Returns the full secret ONCE.\n\n**Auth:** permission:apitokens.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/apitokens -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -990,7 +342,7 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: name (required), expiresat (date/datetime), scopes[permission names, ceiling = owner's permissions], resourcescopes[resource names, plugin-defined]" + "description": "body: JSON (fields not yet documented)" } } } @@ -998,12 +350,12 @@ } }, "/api/apitokens/{tokenid}": { - "put": { + "delete": { "tags": [ - "core-identity" + "core-apitokens" ], - "summary": "Rename, rescope, or (de)activate a token; scope ceiling is always the owner, even when an admin edits", - "description": "Rename, rescope, or (de)activate a token; scope ceiling is always the owner, even when an admin edits\n\n**Auth:** jwt + require_permission apitokens.create (own token, or any if admin)\n\n**Params:** path: tokenid; body: name, isactive (bool), scopes[names, validated against the token OWNER's permissions], resourcescopes[names]\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/apitokens/4 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"isactive\":false}'\n```", + "summary": "Revoke (deactivate) a token. Own token, or any if admin.", + "description": "Revoke (deactivate) a token. Own token, or any if admin.\n\n**Auth:** permission:apitokens.create\n\n**Params:** path: tokenid (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/apitokens/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -1042,29 +394,17 @@ "in": "path", "required": true, "schema": { - "type": "string" + "type": "integer" } } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: tokenid; body: name, isactive (bool), scopes[names, validated against the token OWNER's permissions], resourcescopes[names]" - } - } - } - } + ] }, - "delete": { + "put": { "tags": [ - "core-identity" + "core-apitokens" ], - "summary": "Revoke (deactivate, not delete) a token; audit-logged", - "description": "Revoke (deactivate, not delete) a token; audit-logged\n\n**Auth:** jwt + require_permission apitokens.create (own token, or any if admin)\n\n**Params:** path: tokenid\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/apitokens/4 -H \"Authorization: Bearer $TOK\"\n```", + "summary": "Rename, rescope, or deactivate a token. Own token, or any if admin.", + "description": "Rename, rescope, or deactivate a token. Own token, or any if admin.\n\n**Auth:** permission:apitokens.create\n\n**Params:** path: tokenid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/apitokens/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -1102,1812 +442,6 @@ "name": "tokenid", "in": "path", "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/setup/needs-admin": { - "get": { - "tags": [ - "core-identity" - ], - "summary": "Return {needsadmin: true} when zero users exist, so the login screen can offer first-run setup", - "description": "Return {needsadmin: true} when zero users exist, so the login screen can offer first-run setup\n\n**Auth:** none\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/setup/needs-admin\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/setup/create-admin": { - "post": { - "tags": [ - "core-identity" - ], - "summary": "Bootstrap the very first admin account (creates the admin role if missing)", - "description": "Bootstrap the very first admin account (creates the admin role if missing)\n\n**Auth:** none (only functions while zero users exist; 403 afterwards)\n\n**Params:** body: username, email, password (all required)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/setup/create-admin -H 'Content-Type: application/json' -d '{\"username\":\"admin\",\"email\":\"admin@example.com\",\"password\":\"ChangeMe123\"}'\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: username, email, password (all required)" - } - } - } - } - } - }, - "/api/setup/seed-reference": { - "post": { - "tags": [ - "core-identity" - ], - "summary": "Idempotently seed core reference data, permissions, and default settings (runs the flask seed CLI routines)", - "description": "Idempotently seed core reference data, permissions, and default settings (runs the flask seed CLI routines)\n\n**Auth:** jwt + require_role admin\n\n**Params:** none\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/setup/seed-reference -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "none" - } - } - } - } - } - }, - "/api/setup/seed-starter": { - "post": { - "tags": [ - "core-identity" - ], - "summary": "Idempotently add a starter list of common hardware vendors (Dell, HP, Lenovo, ...)", - "description": "Idempotently add a starter list of common hardware vendors (Dell, HP, Lenovo, ...)\n\n**Auth:** jwt + require_role admin\n\n**Params:** none\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/setup/seed-starter -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "none" - } - } - } - } - } - }, - "/api/setup/complete": { - "post": { - "tags": [ - "core-identity" - ], - "summary": "Set the setup_complete setting to true, marking the first-run wizard finished", - "description": "Set the setup_complete setting to true, marking the first-run wizard finished\n\n**Auth:** jwt + require_role admin\n\n**Params:** none\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/setup/complete -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "none" - } - } - } - } - } - }, - "/api/settings": { - "get": { - "tags": [ - "core-identity" - ], - "summary": "List settings; unauthenticated callers see only the public allowlist (branding+map categories plus named bootstrap...", - "description": "List settings; unauthenticated callers see only the public allowlist (branding+map categories plus named bootstrap keys); secrets always masked as ********\n\n**Auth:** jwt-optional\n\n**Params:** query: category (filter)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/settings?category=branding'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "core-identity" - ], - "summary": "Create a new setting; 409 if the key exists", - "description": "Create a new setting; 409 if the key exists\n\n**Auth:** jwt + require_permission settings.edit\n\n**Params:** body: key (required), value, valuetype (default string), category (default general), description\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/settings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"key\":\"facility_name\",\"value\":\"West Jefferson\",\"category\":\"site\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: key (required), value, valuetype (default string), category (default general), description" - } - } - } - } - } - }, - "/api/settings/{key}": { - "get": { - "tags": [ - "core-identity" - ], - "summary": "Get one setting; non-public keys return 404 (not 403) to unauthenticated callers; secret values masked", - "description": "Get one setting; non-public keys return 404 (not 403) to unauthenticated callers; secret values masked\n\n**Auth:** jwt-optional\n\n**Params:** path: key\n\n**Example:**\n```\ncurl http://localhost:5001/api/settings/facility_name\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "key", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "core-identity" - ], - "summary": "Update a setting (upserts a plugin-scoped string row if the key is new); audit-logged with secrets masked; invalidates...", - "description": "Update a setting (upserts a plugin-scoped string row if the key is new); audit-logged with secrets masked; invalidates the 5-min settings cache\n\n**Auth:** jwt + require_permission settings.edit\n\n**Params:** path: key; body: value (required; bool coerced to 'true'/'false'; secret mask ******** means leave unchanged)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/settings/facility_name -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"value\":\"West Jefferson\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "key", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: key; body: value (required; bool coerced to 'true'/'false'; secret mask ******** means leave unchanged)" - } - } - } - } - } - }, - "/api/settings/seed": { - "post": { - "tags": [ - "core-identity" - ], - "summary": "Idempotently create any missing default settings (identifier toggles, search toggles, map, SMTP, SAML, etc.)", - "description": "Idempotently create any missing default settings (identifier toggles, search toggles, map, SMTP, SAML, etc.)\n\n**Auth:** jwt + require_permission settings.edit\n\n**Params:** none\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/settings/seed -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "none" - } - } - } - } - } - }, - "/api/settings/test-email": { - "post": { - "tags": [ - "core-identity" - ], - "summary": "Send a test email to verify SMTP config; always 200 with a sent flag, SMTP errors returned with credentials scrubbed", - "description": "Send a test email to verify SMTP config; always 200 with a sent flag, SMTP errors returned with credentials scrubbed\n\n**Auth:** jwt + require_permission settings.edit\n\n**Params:** body: to (optional; falls back to alert_recipients setting)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/settings/test-email -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"to\":\"me@example.com\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: to (optional; falls back to alert_recipients setting)" - } - } - } - } - } - }, - "/api/settings/map-blueprint": { - "post": { - "tags": [ - "core-identity" - ], - "summary": "Upload a floor-map blueprint image to the instance maps dir and point map_blueprint_ at its served URL", - "description": "Upload a floor-map blueprint image to the instance maps dir and point map_blueprint_ at its served URL\n\n**Auth:** jwt + require_role admin\n\n**Params:** multipart/form-data: file (png/jpg/jpeg/gif/webp/svg), theme=light|dark\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/settings/map-blueprint -H \"Authorization: Bearer $TOK\" -F 'file=@floor.png' -F 'theme=light'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "multipart/form-data: file (png/jpg/jpeg/gif/webp/svg), theme=light|dark" - } - } - } - } - } - }, - "/api/settings/map-blueprint/{filename}": { - "get": { - "tags": [ - "core-identity" - ], - "summary": "Serve an uploaded floor-map blueprint image (public so kiosk dashboards can load it)", - "description": "Serve an uploaded floor-map blueprint image (public so kiosk dashboards can load it)\n\n**Auth:** none\n\n**Params:** path: filename\n\n**Example:**\n```\ncurl -O http://localhost:5001/api/settings/map-blueprint/blueprint-light.png\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "filename", - "in": "path", - "required": true, - "schema": { - "type": "string", - "description": "may contain slashes" - } - } - ] - } - }, - "/api/settings/branding-logo": { - "post": { - "tags": [ - "core-identity" - ], - "summary": "Upload a branding logo/favicon to the instance branding dir and set the matching branding setting...", - "description": "Upload a branding logo/favicon to the instance branding dir and set the matching branding setting (site_logo/qr_logo/badge_logo/site_favicon)\n\n**Auth:** jwt + require_role admin\n\n**Params:** multipart/form-data: file (map image types plus .ico), kind=site|qr|badge|favicon\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/settings/branding-logo -H \"Authorization: Bearer $TOK\" -F 'file=@logo.svg' -F 'kind=site'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "multipart/form-data: file (map image types plus .ico), kind=site|qr|badge|favicon" - } - } - } - } - } - }, - "/api/settings/branding/{filename}": { - "get": { - "tags": [ - "core-identity" - ], - "summary": "Serve an uploaded branding logo (public - kiosks and print pages read it)", - "description": "Serve an uploaded branding logo (public - kiosks and print pages read it)\n\n**Auth:** none\n\n**Params:** path: filename\n\n**Example:**\n```\ncurl -O http://localhost:5001/api/settings/branding/logo-site.svg\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "filename", - "in": "path", - "required": true, - "schema": { - "type": "string", - "description": "may contain slashes" - } - } - ] - } - }, - "/api/settings/asset-import/script": { - "get": { - "tags": [ - "core-identity" - ], - "summary": "Download Collect-ShopDB.ps1 with this site CSV path already embedded, so the collector script needs no hand editing...", - "description": "Download Collect-ShopDB.ps1 with this site CSV path already embedded, so the collector script needs no hand editing before it runs\n\n**Auth:** admin role\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/settings/asset-import/script -o Collect-ShopDB.ps1\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/settings/asset-import/templates": { - "get": { - "tags": [ - "core-identity" - ], - "summary": "List the importable CSV tables in the order they must be uploaded - foreign keys mean the order is not optional", - "description": "List the importable CSV tables in the order they must be uploaded - foreign keys mean the order is not optional\n\n**Auth:** admin role\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/settings/asset-import/templates\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/settings/asset-import/templates/{tablename}": { - "get": { - "tags": [ - "core-identity" - ], - "summary": "Download a generated CSV template for one importable table, with its real column names as the header row", - "description": "Download a generated CSV template for one importable table, with its real column names as the header row\n\n**Auth:** admin role\n\n**Params:** path: tablename\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/settings/asset-import/templates/assets -o assets.csv\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "tablename", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/settings/asset-import/import": { - "post": { - "tags": [ - "core-identity" - ], - "summary": "Validate uploaded CSVs and, only when commit=true, load them. The default is a dry run that reports what WOULD change...", - "description": "Validate uploaded CSVs and, only when commit=true, load them. The default is a dry run that reports what WOULD change; a committing run writes an AuditLog row with the reconciled count\n\n**Auth:** admin role\n\n**Params:** multipart: one file per table; form: commit (false = validate only)\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -F \"assets=@assets.csv\" -F \"commit=true\" http://localhost:5001/api/settings/asset-import/import\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "multipart: one file per table; form: commit (false = validate only)" - } - } - } - } - } - }, - "/api/settings/asset-import/winc/preview": { - "post": { - "tags": [ - "core-identity" - ], - "summary": "Dry run a WINC machine-adoption import. Writes nothing; returns the plan, a summary, the needs-create groups an admin...", - "description": "Dry run a WINC machine-adoption import. Writes nothing; returns the plan, a summary, the needs-create groups an admin must acknowledge, and the planhash that authorises the matching commit\n\n**Auth:** jwt + require_role admin\n\n**Params:** multipart/form-data: files (one WINC/Maximo CSV export)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/settings/asset-import/winc/preview -H \"Authorization: Bearer $TOK\" -F files=@winc.csv\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "multipart/form-data: files (one WINC/Maximo CSV export)" - } - } - } - } - } - }, - "/api/settings/asset-import/winc/commit": { - "post": { - "tags": [ - "core-identity" - ], - "summary": "Apply a previewed WINC plan in one transaction, journalled so it can be undone. planhash is required and must match a...", - "description": "Apply a previewed WINC plan in one transaction, journalled so it can be undone. planhash is required and must match a freshly rebuilt plan: without it the drift check compared a packet against the plan it was built from and could never fail, so an admin could approve one plan and apply another\n\n**Auth:** jwt + require_role admin\n\n**Params:** multipart/form-data: files (the same export); form: planhash (REQUIRED, from the preview), acknowledge (comma-separated needs-create group names)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/settings/asset-import/winc/commit -H \"Authorization: Bearer $TOK\" -F files=@winc.csv -F planhash=$HASH -F acknowledge=businessunit\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "multipart/form-data: files (the same export); form: planhash (REQUIRED, from the preview), acknowledge (comma-separated needs-create group names)" - } - } - } - } - } - }, - "/api/settings/asset-import/winc/batches": { - "get": { - "tags": [ - "core-identity" - ], - "summary": "List applied and undone import batches, newest first, with kind, row count, who applied it and when", - "description": "List applied and undone import batches, newest first, with kind, row count, who applied it and when\n\n**Auth:** jwt + require_role admin\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/settings/asset-import/winc/batches -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/settings/asset-import/winc/undo": { - "post": { - "tags": [ - "core-identity" - ], - "summary": "Reverse an applied batch by replaying its journal backwards. Conservative: a cell is put back only if it still holds...", - "description": "Reverse an applied batch by replaying its journal backwards. Conservative: a cell is put back only if it still holds what the import wrote, and a created row is deleted only when nothing outside the batch references it\n\n**Auth:** jwt + require_role admin\n\n**Params:** body: batchid (required)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/settings/asset-import/winc/undo -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"batchid\":\"...\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: batchid (required)" - } - } - } - } - } - }, - "/api/reports": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "List all available reports (6 core cards plus cards contributed by enabled plugins via the get_reports hook)", - "description": "List all available reports (6 core cards plus cards contributed by enabled plugins via the get_reports hook)\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/reports\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/reports/machines-by-type": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Machine count grouped by machine type (dualpath secondary bays collapsed when the site setting is on); 404 if machines...", - "description": "Machine count grouped by machine type (dualpath secondary bays collapsed when the site setting is on); 404 if machines plugin absent\n\n**Auth:** jwt-optional\n\n**Params:** businessunitid (int filter), format=json|csv\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/reports/machines-by-type?businessunitid=2&format=csv'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/reports/assets-by-status": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Asset count grouped by status with status colors", - "description": "Asset count grouped by status with status colors\n\n**Auth:** jwt-optional\n\n**Params:** assettypeid, businessunitid, format=json|csv\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/reports/assets-by-status?assettypeid=1'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/reports/kb-popularity": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Most-clicked knowledge base articles; 503 if knowledgebase plugin absent", - "description": "Most-clicked knowledge base articles; 503 if knowledgebase plugin absent\n\n**Auth:** jwt-optional\n\n**Params:** limit (default 20, max 100), format=json|csv\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/reports/kb-popularity?limit=10'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/reports/software-compliance": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Required applications vs installed per PC with compliance rate and up to 100 non-compliant PCs per app; 503 if...", - "description": "Required applications vs installed per PC with compliance rate and up to 100 non-compliant PCs per app; 503 if computers plugin absent\n\n**Auth:** jwt-optional\n\n**Params:** appid (filter to one app), format=json|csv\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/reports/software-compliance?appid=5'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/reports/asset-inventory": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Complete asset inventory summary broken down by type, status, and location", - "description": "Complete asset inventory summary broken down by type, status, and location\n\n**Auth:** jwt-optional\n\n**Params:** businessunitid, locationid, format=json|csv\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/reports/asset-inventory?locationid=3&format=csv'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/reports/pc-relationships": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "PC-to-shop-floor-machine relationships (machine number, vendor, model, hostname, IP) matched in both edge directions...", - "description": "PC-to-shop-floor-machine relationships (machine number, vendor, model, hostname, IP) matched in both edge directions via raw SQL UNION\n\n**Auth:** jwt-optional\n\n**Params:** format=json|csv\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/reports/pc-relationships?format=csv'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/reports/email": { - "post": { - "tags": [ - "core-platform" - ], - "summary": "Email a report's rows as an HTML table on demand; recipient defaults to the site Alert Recipients setting; intended...", - "description": "Email a report's rows as an HTML table on demand; recipient defaults to the site Alert Recipients setting; intended cron target via a PAT scoped to reports.export\n\n**Auth:** jwt + permission:reports.export\n\n**Params:** body: subject, columns [{key,label}], rows [{..}], intro (optional), to (optional email)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/reports/email -H \"Authorization: Bearer $PAT\" -H 'Content-Type: application/json' -d '{\"subject\":\"Warranty Report\",\"columns\":[{\"key\":\"vendor\",\"label\":\"Vendor\"}],\"rows\":[{\"vendor\":\"Haas\"}],\"to\":\"ops@example.com\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: subject, columns [{key,label}], rows [{..}], intro (optional), to (optional email)" - } - } - } - } - } - }, - "/api/search": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Global search across assets (including the gaugelabreference and maintenancereference identifiers), applications, KB...", - "description": "Global search across assets (including the gaugelabreference and maintenancereference identifiers), applications, KB, employees, USB devices, printed items, notifications, custom fields, hostnames, IPs/subnets, vendor/model/type; ServiceNOW ticket prefixes and smart redirects; results capped at 50, types filterable via search__enabled settings. Retired (isactive=0) rows are excluded everywhere, including KB articles whose topic application is retired\n\n**Auth:** jwt-optional\n\n**Params:** q (required, 2-200 chars)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/search?q=WKSTN0042'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/search/suggest": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "A better spelling for a query that found nothing, or null. Its own endpoint so any list page can ask after rendering no...", - "description": "A better spelling for a query that found nothing, or null. Its own endpoint so any list page can ask after rendering no rows, rather than a suggestion key added to every search route. The vocabulary is harvested from searched columns (asset numbers and names, vendors, models, applications, asset types) and cached, so site-specific words are correctable; digits must match exactly, which keeps CSF16 from being 'corrected' to CSF17. Suggests, never rewrites\n\n**Auth:** jwt-optional\n\n**Params:** q (required, 2-200 chars)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/search/suggest?q=keyecne'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/dashboard": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Dashboard summary: asset counts by type (machines/PCs/network/printers/measuring tools, dualpath-collapsed), counts by...", - "description": "Dashboard summary: asset counts by type (machines/PCs/network/printers/measuring tools, dualpath-collapsed), counts by status, 10 most recent assets\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboard\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/dashboard/summary": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Alias route for the same dashboard summary handler as GET /api/dashboard", - "description": "Alias route for the same dashboard summary handler as GET /api/dashboard\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboard/summary\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/dashboard/stats": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Asset counts grouped by every asset type with display category labels", - "description": "Asset counts grouped by every asset type with display category labels\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboard/stats\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/dashboard/navigation": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Sidebar navigation items: core entries merged with get_navigation_items from every enabled plugin, sorted by position", - "description": "Sidebar navigation items: core entries merged with get_navigation_items from every enabled plugin, sorted by position\n\n**Auth:** none\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboard/navigation\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/dashboard/capabilities": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Booleans describing what THIS caller may reach, so the app chrome can avoid drawing controls that would only answer...", - "description": "Booleans describing what THIS caller may reach, so the app chrome can avoid drawing controls that would only answer 401; cansearch reports whether the caller holds assets.view, which a signed-out visitor does when the guest role has been granted it\n\n**Auth:** none\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboard/capabilities\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/dashboard/widgets": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Dashboard widget definitions aggregated from enabled plugins (get_dashboard_widgets hook), sorted by position", - "description": "Dashboard widget definitions aggregated from enabled plugins (get_dashboard_widgets hook), sorted by position\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboard/widgets\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/dashboard/health": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Health check: runs SELECT 1 against the DB, returns ok/degraded plus app version", - "description": "Health check: runs SELECT 1 against the DB, returns ok/degraded plus app version\n\n**Auth:** none\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboard/health\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/dashboarddefaults/visitor-location": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Resolve the business unit for a kiosk/lobby display by FQDN first then IP (caller IP used when ipaddress omitted); null...", - "description": "Resolve the business unit for a kiosk/lobby display by FQDN first then IP (caller IP used when ipaddress omitted); null businessunitid when unmapped\n\n**Auth:** none\n\n**Params:** fqdn (optional), ipaddress (optional, defaults to client IP)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/dashboarddefaults/visitor-location?fqdn=display01.example.net'\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/dashboarddefaults/display-role": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Resolve what a display PC should show at boot: role (dashboard/lobby/partskiosk), frontend path, and business unit...", - "description": "Resolve what a display PC should show at boot: role (dashboard/lobby/partskiosk), frontend path, and business unit; null role when unmapped\n\n**Auth:** none\n\n**Params:** fqdn (optional), ipaddress (optional, defaults to client IP)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/dashboarddefaults/display-role?ipaddress=10.1.2.3'\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/dashboarddefaults": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "List all active display-to-business-unit mappings", - "description": "List all active display-to-business-unit mappings\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboarddefaults\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "core-platform" - ], - "summary": "Create a display mapping; requires fqdn or ipaddress, dashboard role requires businessunitid, 409 on duplicate fqdn/IP...", - "description": "Create a display mapping; requires fqdn or ipaddress, dashboard role requires businessunitid, 409 on duplicate fqdn/IP; audit-logged\n\n**Auth:** jwt + role:admin\n\n**Params:** body: fqdn, ipaddress, displayrole (dashboard|lobby|partskiosk, default dashboard), businessunitid, description\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/dashboarddefaults -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"ipaddress\":\"10.1.2.3\",\"displayrole\":\"dashboard\",\"businessunitid\":2}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: fqdn, ipaddress, displayrole (dashboard|lobby|partskiosk, default dashboard), businessunitid, description" - } - } - } - } - } - }, - "/api/dashboarddefaults/{default_id}": { - "put": { - "tags": [ - "core-platform" - ], - "summary": "Update a display mapping; non-dashboard roles get businessunitid nulled, dashboard role must keep one", - "description": "Update a display mapping; non-dashboard roles get businessunitid nulled, dashboard role must keep one\n\n**Auth:** jwt + role:admin\n\n**Params:** body: any of fqdn, ipaddress, displayrole, businessunitid, description\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/dashboarddefaults/7 -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"displayrole\":\"lobby\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "default_id", - "in": "path", - "required": true, "schema": { "type": "integer" } @@ -2920,18 +454,106 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: any of fqdn, ipaddress, displayrole, businessunitid, description" + "description": "path: tokenid (int); body: JSON (fields not yet documented)" } } } } + } + }, + "/api/applications": { + "get": { + "tags": [ + "core-applications" + ], + "summary": "List all applications.", + "description": "List all applications.\n\n**Auth:** permission:applications.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/applications -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } }, + "post": { + "tags": [ + "core-applications" + ], + "summary": "Create a new application.", + "description": "Create a new application.\n\n**Auth:** permission:applications.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/applications -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/applications/{app_id}": { "delete": { "tags": [ - "core-platform" + "core-applications" ], - "summary": "Soft-delete (deactivate) a display mapping", - "description": "Soft-delete (deactivate) a display mapping\n\n**Auth:** jwt + role:admin\n\n**Params:** none\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/dashboarddefaults/7 -H \"Authorization: Bearer $JWT\"\n```", + "summary": "Permanently delete an application, its versions, and its uploads.", + "description": "Permanently delete an application, its versions, and its uploads.\n\n**Auth:** permission:applications.delete\n\n**Params:** path: app_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/applications/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -2966,7 +588,280 @@ }, "parameters": [ { - "name": "default_id", + "name": "app_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "core-applications" + ], + "summary": "Get a single application with details.", + "description": "Get a single application with details.\n\n**Auth:** permission:applications.view\n\n**Params:** path: app_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/applications/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-applications" + ], + "summary": "Update an application.", + "description": "Update an application.\n\n**Auth:** permission:applications.edit\n\n**Params:** path: app_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/applications/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: app_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/applications/{app_id}/image": { + "delete": { + "tags": [ + "core-applications" + ], + "summary": "Remove an application's image and clear the field.", + "description": "Remove an application's image and clear the field.\n\n**Auth:** permission:applications.edit\n\n**Params:** path: app_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/applications/1/image -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "post": { + "tags": [ + "core-applications" + ], + "summary": "Upload (or replace) an application's image.", + "description": "Upload (or replace) an application's image.\n\n**Auth:** permission:applications.edit\n\n**Params:** path: app_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/applications/1/image -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: app_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/applications/{app_id}/installed": { + "get": { + "tags": [ + "core-applications" + ], + "summary": "List all computers that have this application installed.", + "description": "List all computers that have this application installed.\n\n**Auth:** permission:applications.view\n\n**Params:** path: app_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/applications/1/installed -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "app_id", "in": "path", "required": true, "schema": { @@ -2976,16 +871,16 @@ ] } }, - "/api/collector/{pluginname}": { - "post": { + "/api/applications/{app_id}/package": { + "delete": { "tags": [ - "core-platform" + "core-applications" ], - "summary": "Generic collector ingest (ADR-006): schema-validated identity field, idempotent upsert via the plugin's...", - "description": "Generic collector ingest (ADR-006): schema-validated identity field, idempotent upsert via the plugin's apply_collector_payload; audit-logged; 404 when no collector registered for the plugin\n\n**Auth:** api-key (X-API-Key: per-plugin COLLECTOR_API_KEY_ or shared COLLECTOR_API_KEY, or a collector.ingest-scoped managed PAT via Bearer/X-API-Key)\n\n**Params:** body: JSON payload whose schema identityfield (e.g. hostname) is required; rest is plugin-defined\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/collector/computers -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{\"hostname\":\"WKSTN0042\",\"serialnumber\":\"ABC123\"}'\n```", + "summary": "Remove an uploaded installer and clear the path it set.", + "description": "Remove an uploaded installer and clear the path it set.\n\n**Auth:** permission:applications.edit\n\n**Params:** path: app_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/applications/1/package -H \"Authorization: Bearer $TOK\"\n```", "security": [ { - "apiKeyAuth": [] + "bearerAuth": [] } ], "responses": { @@ -3017,11 +912,60 @@ }, "parameters": [ { - "name": "pluginname", + "name": "app_id", "in": "path", "required": true, "schema": { - "type": "string" + "type": "integer" + } + } + ] + }, + "post": { + "tags": [ + "core-applications" + ], + "summary": "Upload (or replace) the installer for an application.", + "description": "Upload (or replace) the installer for an application.\n\n**Auth:** permission:applications.edit\n\n**Params:** path: app_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/applications/1/package -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" } } ], @@ -3032,20 +976,929 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: JSON payload whose schema identityfield (e.g. hostname) is required; rest is plugin-defined" + "description": "path: app_id (int); body: JSON (fields not yet documented)" } } } } } }, - "/api/collector/_schemas": { + "/api/applications/{app_id}/revert": { + "post": { + "tags": [ + "core-applications" + ], + "summary": "Strip an application back to a catalogue record.", + "description": "Strip an application back to a catalogue record.\n\n**Auth:** permission:applications.edit\n\n**Params:** path: app_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/applications/1/revert -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: app_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/applications/{app_id}/versions": { "get": { "tags": [ - "core-platform" + "core-applications" ], - "summary": "List collector schemas for all enabled plugins that accept collector input", - "description": "List collector schemas for all enabled plugins that accept collector input\n\n**Auth:** jwt\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/collector/_schemas -H \"Authorization: Bearer $JWT\"\n```", + "summary": "List all versions for an application.", + "description": "List all versions for an application.\n\n**Auth:** permission:applications.view\n\n**Params:** path: app_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/applications/1/versions -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "post": { + "tags": [ + "core-applications" + ], + "summary": "Create a new version for an application.", + "description": "Create a new version for an application.\n\n**Auth:** permission:applications.create\n\n**Params:** path: app_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/applications/1/versions -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: app_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/applications/image/{filename}": { + "get": { + "tags": [ + "core-applications" + ], + "summary": "Serve an uploaded application image.", + "description": "Serve an uploaded application image.\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** path: filename (path)\n\n**Example:**\n```\ncurl http://localhost:5001/api/applications/image/example.png\n```", + "security": [], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "filename", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "may contain slashes" + } + } + ] + } + }, + "/api/applications/machines/{machine_id}": { + "get": { + "tags": [ + "core-applications" + ], + "summary": "List all applications installed on a computer.", + "description": "List all applications installed on a computer.\n\n**Auth:** permission:applications.view\n\n**Params:** path: machine_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/applications/machines/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "machine_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "post": { + "tags": [ + "core-applications" + ], + "summary": "Install an application on a computer.", + "description": "Install an application on a computer.\n\n**Auth:** permission:applications.create\n\n**Params:** path: machine_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/applications/machines/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "machine_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: machine_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/applications/machines/{machine_id}/{app_id}": { + "delete": { + "tags": [ + "core-applications" + ], + "summary": "Uninstall an application from a computer.", + "description": "Uninstall an application from a computer.\n\n**Auth:** permission:applications.delete\n\n**Params:** path: machine_id (int), app_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/applications/machines/1/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "machine_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-applications" + ], + "summary": "Update installed application (e.g., change version).", + "description": "Update installed application (e.g., change version).\n\n**Auth:** permission:applications.edit\n\n**Params:** path: machine_id (int), app_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/applications/machines/1/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "machine_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: machine_id (int), app_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/applications/package/{filename}": { + "get": { + "tags": [ + "core-applications" + ], + "summary": "Download an uploaded installer.", + "description": "Download an uploaded installer.\n\n**Auth:** permission:applications.download\n\n**Params:** path: filename (path)\n\n**Example:**\n```\ncurl http://localhost:5001/api/applications/package/example.png -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "filename", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "may contain slashes" + } + } + ] + } + }, + "/api/assets": { + "get": { + "tags": [ + "core-assets" + ], + "summary": "List all assets with filtering and pagination.", + "description": "List all assets with filtering and pagination.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "core-assets" + ], + "summary": "Create a new asset.", + "description": "Create a new asset.\n\n**Auth:** permission:assets.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/assets -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/assets/{asset_id}": { + "delete": { + "tags": [ + "core-assets" + ], + "summary": "Delete (soft delete) an asset.", + "description": "Delete (soft delete) an asset.\n\n**Auth:** permission:assets.delete\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/assets/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "core-assets" + ], + "summary": "Get a single asset with full details.", + "description": "Get a single asset with full details.\n\n**Auth:** permission:assets.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-assets" + ], + "summary": "Update an asset.", + "description": "Update an asset.\n\n**Auth:** permission:assets.edit\n\n**Params:** path: asset_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/assets/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: asset_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/assets/{asset_id}/communications": { + "get": { + "tags": [ + "core-assets" + ], + "summary": "Get all communications for an asset.", + "description": "Get all communications for an asset.\n\n**Auth:** permission:assets.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/1/communications -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/assets/{asset_id}/relationships": { + "get": { + "tags": [ + "core-assets" + ], + "summary": "Get all relationships for an asset.", + "description": "Get all relationships for an asset.\n\n**Auth:** permission:assets.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/1/relationships -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/assets/lookup/{assetnumber}": { + "get": { + "tags": [ + "core-assets" + ], + "summary": "Look up an asset by its asset number.", + "description": "Look up an asset by its asset number.\n\n**Auth:** permission:assets.view\n\n**Params:** path: assetnumber (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/lookup/x -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "assetnumber", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ] + } + }, + "/api/assets/map": { + "get": { + "tags": [ + "core-assets" + ], + "summary": "Get all assets with map positions for unified floor map display.", + "description": "Get all assets with map positions for unified floor map display.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/map -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -3077,16 +1930,16 @@ } } }, - "/api/collector/pc": { + "/api/assets/relationships": { "post": { "tags": [ - "core-platform" + "core-assets" ], - "summary": "Legacy computers-specific ingest: update one PC matched by hostname (or asset number) - lastreporteddate, lastboottime...", - "description": "Legacy computers-specific ingest: update one PC matched by hostname (or asset number) - lastreporteddate, lastboottime, loggedinuser, serialnumber\n\n**Auth:** api-key\n\n**Params:** body: hostname (required), lastboottime (ISO), currentuser, serialnumber\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/collector/pc -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{\"hostname\":\"WKSTN0042\",\"currentuser\":\"212345678\"}'\n```", + "summary": "Create a relationship between two assets.", + "description": "Create a relationship between two assets.\n\n**Auth:** permission:assets.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/assets/relationships -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { - "apiKeyAuth": [] + "bearerAuth": [] } ], "responses": { @@ -3120,23 +1973,23 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: hostname (required), lastboottime (ISO), currentuser, serialnumber" + "description": "body: JSON (fields not yet documented)" } } } } } }, - "/api/collector/apps": { - "post": { + "/api/assets/relationships/{rel_id}": { + "delete": { "tags": [ - "core-platform" + "core-assets" ], - "summary": "Update installed applications for one PC; only apps already in the Application table are tracked, others skipped...", - "description": "Update installed applications for one PC; only apps already in the Application table are tracked, others skipped; returns created/updated/skipped counts\n\n**Auth:** api-key\n\n**Params:** body: hostname (required), apps [{appname, version}] (required)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/collector/apps -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{\"hostname\":\"WKSTN0042\",\"apps\":[{\"appname\":\"PC-DMIS\",\"version\":\"2023.2\"}]}'\n```", + "summary": "Delete an asset relationship.", + "description": "Delete an asset relationship.\n\n**Auth:** permission:assets.delete\n\n**Params:** path: rel_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/assets/relationships/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { - "apiKeyAuth": [] + "bearerAuth": [] } ], "responses": { @@ -3161,132 +2014,33 @@ }, "403": { "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." } }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: hostname (required), apps [{appname, version}] (required)" - } - } - } - } - } - }, - "/api/collector/heartbeat": { - "post": { - "tags": [ - "core-platform" - ], - "summary": "Record PC online heartbeat (single hostname or batch); stamps lastreporteddate, returns updated count and notfound list", - "description": "Record PC online heartbeat (single hostname or batch); stamps lastreporteddate, returns updated count and notfound list\n\n**Auth:** api-key\n\n**Params:** body: hostname (string) or hostnames (array)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/collector/heartbeat -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{\"hostnames\":[\"pc1\",\"pc2\"]}'\n```", - "security": [ + "parameters": [ { - "apiKeyAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: hostname (string) or hostnames (array)" - } + "name": "rel_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" } } - } + ] } }, - "/api/collector/bulk": { - "post": { - "tags": [ - "core-platform" - ], - "summary": "Bulk update many PCs in one call (lastreporteddate, currentuser, lastboottime per entry); returns...", - "description": "Bulk update many PCs in one call (lastreporteddate, currentuser, lastboottime per entry); returns updated/notfound/errors\n\n**Auth:** api-key\n\n**Params:** body: pcs [{hostname (required), currentuser, lastboottime}]\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/collector/bulk -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{\"pcs\":[{\"hostname\":\"pc1\",\"currentuser\":\"212345678\"}]}'\n```", - "security": [ - { - "apiKeyAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: pcs [{hostname (required), currentuser, lastboottime}]" - } - } - } - } - } - }, - "/api/collector/status": { + "/api/assets/relationshiptypes": { "get": { "tags": [ - "core-platform" + "core-assets" ], - "summary": "Collector API liveness/credential check; returns timestamp and the collector endpoint list", - "description": "Collector API liveness/credential check; returns timestamp and the collector endpoint list\n\n**Auth:** api-key\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/collector/status -H \"X-API-Key: $KEY\"\n```", + "summary": "List all asset relationship types.", + "description": "List all asset relationship types.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/relationshiptypes -H \"Authorization: Bearer $TOK\"\n```", "security": [ { - "apiKeyAuth": [] + "bearerAuth": [] } ], "responses": { @@ -3313,15 +2067,620 @@ "description": "Authenticated, but not permitted." } } + }, + "post": { + "tags": [ + "core-assets" + ], + "summary": "Create a new asset relationship type.", + "description": "Create a new asset relationship type.\n\n**Auth:** permission:assets.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/assets/relationshiptypes -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/assets/relationshiptypes/{type_id}": { + "delete": { + "tags": [ + "core-assets" + ], + "summary": "Delete a relationship type. Refused if any relationship still uses it.", + "description": "Delete a relationship type. Refused if any relationship still uses it.\n\n**Auth:** permission:assets.delete\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/assets/relationshiptypes/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-assets" + ], + "summary": "Update a relationship type.", + "description": "Update a relationship type.\n\n**Auth:** permission:assets.edit\n\n**Params:** path: type_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/assets/relationshiptypes/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: type_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/assets/statuses": { + "get": { + "tags": [ + "core-assets" + ], + "summary": "List all asset statuses.", + "description": "List all asset statuses.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/statuses -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "core-assets" + ], + "summary": "Create a new asset status.", + "description": "Create a new asset status.\n\n**Auth:** permission:assets.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/assets/statuses -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/assets/statuses/{status_id}": { + "delete": { + "tags": [ + "core-assets" + ], + "summary": "Delete an asset status. Refused if any asset still uses it.", + "description": "Delete an asset status. Refused if any asset still uses it.\n\n**Auth:** permission:assets.delete\n\n**Params:** path: status_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/assets/statuses/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "status_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "core-assets" + ], + "summary": "Get a single asset status.", + "description": "Get a single asset status.\n\n**Auth:** permission:assets.view\n\n**Params:** path: status_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/statuses/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "status_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-assets" + ], + "summary": "Update an asset status.", + "description": "Update an asset status.\n\n**Auth:** permission:assets.edit\n\n**Params:** path: status_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/assets/statuses/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "status_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: status_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/assets/types": { + "get": { + "tags": [ + "core-assets" + ], + "summary": "List all asset types.", + "description": "List all asset types.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/types -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "core-assets" + ], + "summary": "Create a new asset type.", + "description": "Create a new asset type.\n\n**Auth:** permission:assets.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/assets/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/assets/types/{type_id}": { + "get": { + "tags": [ + "core-assets" + ], + "summary": "Get a single asset type.", + "description": "Get a single asset type.\n\n**Auth:** permission:assets.view\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/types/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-assets" + ], + "summary": "Update an asset type's display fields (color/icon/description). The name, plugin, and table are structural and not...", + "description": "Update an asset type's display fields (color/icon/description). The name, plugin, and table are structural and not editable here.\n\n**Auth:** permission:assets.edit\n\n**Params:** path: type_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/assets/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: type_id (int); body: JSON (fields not yet documented)" + } + } + } + } } }, "/api/auditlogs": { "get": { "tags": [ - "core-platform" + "core-auditlogs" ], - "summary": "List audit logs with filtering and pagination, newest first; rows enriched with best-effort SSO-to-full-name resolution", - "description": "List audit logs with filtering and pagination, newest first; rows enriched with best-effort SSO-to-full-name resolution\n\n**Auth:** jwt + permission:audit.view\n\n**Params:** page (default 1), perpage (default 50, max 200), action (created|updated|deleted), entitytype, userid (int), search (entityname/username ilike), from_date, to_date (ISO)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/auditlogs?action=deleted&perpage=100' -H \"Authorization: Bearer $JWT\"\n```", + "summary": "List audit logs with filtering and pagination.", + "description": "List audit logs with filtering and pagination.\n\n**Auth:** permission:audit.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/auditlogs -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -3356,10 +2715,10 @@ "/api/auditlogs/entity/{entitytype}/{entityid}": { "get": { "tags": [ - "core-platform" + "core-auditlogs" ], - "summary": "Full audit history for one entity, newest first", - "description": "Full audit history for one entity, newest first\n\n**Auth:** jwt + permission:audit.view\n\n**Params:** path only\n\n**Example:**\n```\ncurl http://localhost:5001/api/auditlogs/entity/Asset/42 -H \"Authorization: Bearer $JWT\"\n```", + "summary": "Get audit history for a specific entity.", + "description": "Get audit history for a specific entity.\n\n**Auth:** permission:audit.view\n\n**Params:** path: entitytype (string), entityid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/auditlogs/entity/x/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -3412,89 +2771,13 @@ ] } }, - "/api/auditlogs/stats": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Audit statistics: counts by action and entity type, last-7-days activity count, top 5 most active users", - "description": "Audit statistics: counts by action and entity type, last-7-days activity count, top 5 most active users\n\n**Auth:** jwt + permission:audit.view\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/auditlogs/stats -H \"Authorization: Bearer $JWT\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/auditlogs/retention": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Audit log size (total entries, oldest/newest timestamps) and, when a retention period is configured, how many entries a...", - "description": "Audit log size (total entries, oldest/newest timestamps) and, when a retention period is configured, how many entries a purge would delete. Drives the Site & Facility retention card so the count is seen before anything is deleted\n\n**Auth:** jwt + audit.view\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/auditlogs/retention -H \"Authorization: Bearer $TOKEN\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, "/api/auditlogs/purge": { "post": { "tags": [ - "core-platform" + "core-auditlogs" ], - "summary": "Delete audit entries older than the retention window, in committed batches. Requires audit.purge, not merely...", - "description": "Delete audit entries older than the retention window, in committed batches. Requires audit.purge, not merely audit.view. Batch-capped so the request cannot outlive a proxy timeout; an incomplete run returns complete=false with the number still remaining rather than reporting a finished job. Refuses with 400 when no retention period is configured\n\n**Auth:** jwt + audit.purge\n\n**Params:** body: days (optional, defaults to the audit_retentiondays setting), dryrun (optional bool)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/auditlogs/purge -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"days\":365,\"dryrun\":true}'\n```", + "summary": "Delete entries older than the retention window.", + "description": "Delete entries older than the retention window.\n\n**Auth:** permission:audit.purge\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/auditlogs/purge -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -3531,7 +2814,3995 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: days (optional, defaults to the audit_retentiondays setting), dryrun (optional bool)" + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/auditlogs/retention": { + "get": { + "tags": [ + "core-auditlogs" + ], + "summary": "Size of the log and what the configured retention would remove.", + "description": "Size of the log and what the configured retention would remove.\n\n**Auth:** permission:audit.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/auditlogs/retention -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/auditlogs/stats": { + "get": { + "tags": [ + "core-auditlogs" + ], + "summary": "Get audit log statistics.", + "description": "Get audit log statistics.\n\n**Auth:** permission:audit.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/auditlogs/stats -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/auth/change-password": { + "post": { + "tags": [ + "core-auth" + ], + "summary": "Change the authenticated user's own password.", + "description": "Change the authenticated user's own password.\n\n**Auth:** jwt\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/auth/change-password -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/auth/login": { + "post": { + "tags": [ + "core-auth" + ], + "summary": "Authenticate user and return JWT tokens.", + "description": "Authenticate user and return JWT tokens.\n\n**Auth:** none (the login surface itself; rate limited, 5-strike lockout)\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/auth/login -H 'Content-Type: application/json' -d '{}'\n```", + "security": [], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/auth/logout": { + "post": { + "tags": [ + "core-auth" + ], + "summary": "Logout user (for frontend token cleanup).", + "description": "Logout user (for frontend token cleanup).\n\n**Auth:** jwt\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/auth/logout -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/auth/me": { + "get": { + "tags": [ + "core-auth" + ], + "summary": "Get current authenticated user info.", + "description": "Get current authenticated user info.\n\n**Auth:** jwt\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/auth/me -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/auth/refresh": { + "post": { + "tags": [ + "core-auth" + ], + "summary": "Refresh access token using refresh token.", + "description": "Refresh access token using refresh token.\n\n**Auth:** jwt (refresh token)\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/auth/refresh -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/businessunits": { + "get": { + "tags": [ + "core-businessunits" + ], + "summary": "List all business units.", + "description": "List all business units.\n\n**Auth:** jwt-optional (works signed out; the handler decides what shows)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/businessunits -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + }, + {} + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + } + }, + "post": { + "tags": [ + "core-businessunits" + ], + "summary": "Create a new business unit.", + "description": "Create a new business unit.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/businessunits -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/businessunits/{bu_id}": { + "delete": { + "tags": [ + "core-businessunits" + ], + "summary": "Delete (deactivate) a business unit.", + "description": "Delete (deactivate) a business unit.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: bu_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/businessunits/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "bu_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "core-businessunits" + ], + "summary": "Get a single business unit.", + "description": "Get a single business unit.\n\n**Auth:** permission:assets.view\n\n**Params:** path: bu_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/businessunits/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "bu_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-businessunits" + ], + "summary": "Update a business unit.", + "description": "Update a business unit.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: bu_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/businessunits/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "bu_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: bu_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/collector/{pluginname}": { + "post": { + "tags": [ + "core-collector" + ], + "summary": "Generic collector ingest for any plugin (ADR-006).", + "description": "Generic collector ingest for any plugin (ADR-006).\n\n**Auth:** api-key (per-plugin collector token)\n\n**Params:** path: pluginname (string); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/collector/x -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "pluginname", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: pluginname (string); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/collector/_schemas": { + "get": { + "tags": [ + "core-collector" + ], + "summary": "List collector schemas for all enabled plugins (JWT-protected).", + "description": "List collector schemas for all enabled plugins (JWT-protected).\n\n**Auth:** jwt\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/collector/_schemas -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/collector/apps": { + "post": { + "tags": [ + "core-collector" + ], + "summary": "Update installed applications for a PC (matched by hostname).", + "description": "Update installed applications for a PC (matched by hostname).\n\n**Auth:** api-key (collector token, legacy shared key)\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/collector/apps -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/collector/bulk": { + "post": { + "tags": [ + "core-collector" + ], + "summary": "Bulk update multiple PCs at once.", + "description": "Bulk update multiple PCs at once.\n\n**Auth:** api-key (collector token, legacy shared key)\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/collector/bulk -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/collector/heartbeat": { + "post": { + "tags": [ + "core-collector" + ], + "summary": "Record PC online status / heartbeat (single or batch).", + "description": "Record PC online status / heartbeat (single or batch).\n\n**Auth:** api-key (collector token, legacy shared key)\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/collector/heartbeat -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/collector/pc": { + "post": { + "tags": [ + "core-collector" + ], + "summary": "Update one PC from the collection script (matched by hostname).", + "description": "Update one PC from the collection script (matched by hostname).\n\n**Auth:** api-key (collector token, legacy shared key)\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/collector/pc -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/collector/status": { + "get": { + "tags": [ + "core-collector" + ], + "summary": "Check collector API status.", + "description": "Check collector API status.\n\n**Auth:** api-key (per-plugin collector token)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/collector/status -H \"X-API-Key: $KEY\"\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/customfields": { + "get": { + "tags": [ + "core-customfields" + ], + "summary": "List custom-field definitions. Filter by ?assettypeid=. ?active=false includes inactive ones.", + "description": "List custom-field definitions. Filter by ?assettypeid=. ?active=false includes inactive ones.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/customfields -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "core-customfields" + ], + "summary": "UNDOCUMENTED: customfields.create_field has no docstring.", + "description": "UNDOCUMENTED: customfields.create_field has no docstring.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/customfields -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/customfields/{fieldid}": { + "delete": { + "tags": [ + "core-customfields" + ], + "summary": "UNDOCUMENTED: customfields.delete_field has no docstring.", + "description": "UNDOCUMENTED: customfields.delete_field has no docstring.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: fieldid (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/customfields/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "fieldid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-customfields" + ], + "summary": "UNDOCUMENTED: customfields.update_field has no docstring.", + "description": "UNDOCUMENTED: customfields.update_field has no docstring.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: fieldid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/customfields/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "fieldid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: fieldid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/customfields/asset/{assetid}": { + "get": { + "tags": [ + "core-customfields" + ], + "summary": "Active custom fields for an asset's type, merged with its values.", + "description": "Active custom fields for an asset's type, merged with its values.\n\n**Auth:** permission:assets.view\n\n**Params:** path: assetid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/customfields/asset/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "assetid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-customfields" + ], + "summary": "Upsert values for an asset. Body: {values: {fieldid: value, ...}}.", + "description": "Upsert values for an asset. Body: {values: {fieldid: value, ...}}.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: assetid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/customfields/asset/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "assetid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: assetid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/dashboard": { + "get": { + "tags": [ + "core-dashboard" + ], + "summary": "Get dashboard summary data (asset-based).", + "description": "Get dashboard summary data (asset-based).\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboard -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/dashboard/capabilities": { + "get": { + "tags": [ + "core-dashboard" + ], + "summary": "What THIS caller can reach, for chrome that would otherwise only 401.", + "description": "What THIS caller can reach, for chrome that would otherwise only 401.\n\n**Auth:** jwt-optional (works signed out; the handler decides what shows)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboard/capabilities -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + }, + {} + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + } + } + }, + "/api/dashboard/health": { + "get": { + "tags": [ + "core-dashboard" + ], + "summary": "Health check endpoint (no auth required).", + "description": "Health check endpoint (no auth required).\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboard/health\n```", + "security": [], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + } + } + }, + "/api/dashboard/navigation": { + "get": { + "tags": [ + "core-dashboard" + ], + "summary": "Get navigation items from all loaded plugins.", + "description": "Get navigation items from all loaded plugins.\n\n**Auth:** jwt-optional (works signed out; the handler decides what shows)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboard/navigation -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + }, + {} + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + } + } + }, + "/api/dashboard/stats": { + "get": { + "tags": [ + "core-dashboard" + ], + "summary": "Get detailed statistics by asset type.", + "description": "Get detailed statistics by asset type.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboard/stats -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/dashboard/summary": { + "get": { + "tags": [ + "core-dashboard" + ], + "summary": "Get dashboard summary data (asset-based).", + "description": "Get dashboard summary data (asset-based).\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboard/summary -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/dashboard/widgets": { + "get": { + "tags": [ + "core-dashboard" + ], + "summary": "Aggregate dashboard widget definitions from all enabled plugins.", + "description": "Aggregate dashboard widget definitions from all enabled plugins.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboard/widgets -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/dashboarddefaults": { + "get": { + "tags": [ + "core-dashboarddefaults" + ], + "summary": "List all visitor-IP -> business-unit mappings.", + "description": "List all visitor-IP -> business-unit mappings.\n\n**Auth:** permission:settings.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboarddefaults -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "core-dashboarddefaults" + ], + "summary": "Create a visitor-IP -> business-unit mapping.", + "description": "Create a visitor-IP -> business-unit mapping.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/dashboarddefaults -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/dashboarddefaults/{default_id}": { + "delete": { + "tags": [ + "core-dashboarddefaults" + ], + "summary": "Delete (deactivate) a mapping.", + "description": "Delete (deactivate) a mapping.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: default_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/dashboarddefaults/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "default_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-dashboarddefaults" + ], + "summary": "Update a mapping.", + "description": "Update a mapping.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: default_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/dashboarddefaults/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "default_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: default_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/dashboarddefaults/display-role": { + "get": { + "tags": [ + "core-dashboarddefaults" + ], + "summary": "Resolve what a single 'display' PC should show, from its own IP.", + "description": "Resolve what a single 'display' PC should show, from its own IP.\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboarddefaults/display-role\n```", + "security": [], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + } + } + }, + "/api/dashboarddefaults/visitor-location": { + "get": { + "tags": [ + "core-dashboarddefaults" + ], + "summary": "Resolve the business unit for the calling display by its IP.", + "description": "Resolve the business unit for the calling display by its IP.\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/dashboarddefaults/visitor-location\n```", + "security": [], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + } + } + }, + "/api/docs": { + "get": { + "tags": [ + "core-docs" + ], + "summary": "Redoc page. url_for keeps the asset URLs correct under any mount.", + "description": "Redoc page. url_for keeps the asset URLs correct under any mount.\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/docs/\n```", + "security": [], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + } + } + }, + "/api/docs/llms.txt": { + "get": { + "tags": [ + "core-docs" + ], + "summary": "Concise LLM-oriented API guide.", + "description": "Concise LLM-oriented API guide.\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/docs/llms.txt\n```", + "security": [], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + } + } + }, + "/api/docs/openapi.json": { + "get": { + "tags": [ + "core-docs" + ], + "summary": "The generated OpenAPI 3.1 spec (machine + LLM readable).", + "description": "The generated OpenAPI 3.1 spec (machine + LLM readable).\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/docs/openapi.json\n```", + "security": [], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + } + } + }, + "/api/docs/redoc.standalone.js": { + "get": { + "tags": [ + "core-docs" + ], + "summary": "Vendored Redoc bundle (offline).", + "description": "Vendored Redoc bundle (offline).\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/docs/redoc.standalone.js\n```", + "security": [], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + } + } + }, + "/api/locations": { + "get": { + "tags": [ + "core-locations" + ], + "summary": "List all locations.", + "description": "List all locations.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/locations -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "core-locations" + ], + "summary": "Create a new location.", + "description": "Create a new location.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/locations -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/locations/{location_id}": { + "delete": { + "tags": [ + "core-locations" + ], + "summary": "Delete (deactivate) a location.", + "description": "Delete (deactivate) a location.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: location_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/locations/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "location_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "core-locations" + ], + "summary": "Get a single location.", + "description": "Get a single location.\n\n**Auth:** permission:assets.view\n\n**Params:** path: location_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/locations/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "location_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-locations" + ], + "summary": "Update a location.", + "description": "Update a location.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: location_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/locations/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "location_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: location_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/locations/types": { + "get": { + "tags": [ + "core-locations" + ], + "summary": "List location types. ?active=false includes inactive ones.", + "description": "List location types. ?active=false includes inactive ones.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/locations/types -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "core-locations" + ], + "summary": "UNDOCUMENTED: locations.create_location_type has no docstring.", + "description": "UNDOCUMENTED: locations.create_location_type has no docstring.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/locations/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/locations/types/{type_id}": { + "delete": { + "tags": [ + "core-locations" + ], + "summary": "UNDOCUMENTED: locations.delete_location_type has no docstring.", + "description": "UNDOCUMENTED: locations.delete_location_type has no docstring.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/locations/types/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-locations" + ], + "summary": "UNDOCUMENTED: locations.update_location_type has no docstring.", + "description": "UNDOCUMENTED: locations.update_location_type has no docstring.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: type_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/locations/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: type_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/maplevels": { + "get": { + "tags": [ + "core-maplevels" + ], + "summary": "Every active level, grouped by building, in display order.", + "description": "Every active level, grouped by building, in display order.\n\n**Auth:** jwt-optional (works signed out; the handler decides what shows)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/maplevels -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + }, + {} + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + } + }, + "post": { + "tags": [ + "core-maplevels" + ], + "summary": "UNDOCUMENTED: maplevels.create_level has no docstring.", + "description": "UNDOCUMENTED: maplevels.create_level has no docstring.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/maplevels -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/maplevels/{levelid}": { + "delete": { + "tags": [ + "core-maplevels" + ], + "summary": "Deactivate a level, refusing while assets are still placed on it.", + "description": "Deactivate a level, refusing while assets are still placed on it.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: levelid (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/maplevels/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "levelid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "core-maplevels" + ], + "summary": "UNDOCUMENTED: maplevels.get_level has no docstring.", + "description": "UNDOCUMENTED: maplevels.get_level has no docstring.\n\n**Auth:** jwt-optional (works signed out; the handler decides what shows)\n\n**Params:** path: levelid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/maplevels/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + }, + {} + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "levelid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "patch": { + "tags": [ + "core-maplevels" + ], + "summary": "UNDOCUMENTED: maplevels.update_level has no docstring.", + "description": "UNDOCUMENTED: maplevels.update_level has no docstring.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: levelid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PATCH http://localhost:5001/api/maplevels/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "levelid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: levelid (int); body: JSON (fields not yet documented)" + } + } + } + } + }, + "put": { + "tags": [ + "core-maplevels" + ], + "summary": "UNDOCUMENTED: maplevels.update_level has no docstring.", + "description": "UNDOCUMENTED: maplevels.update_level has no docstring.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: levelid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PATCH http://localhost:5001/api/maplevels/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "levelid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: levelid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/maplevels/{levelid}/blueprint": { + "post": { + "tags": [ + "core-maplevels" + ], + "summary": "Upload this level's blueprint for one theme.", + "description": "Upload this level's blueprint for one theme.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: levelid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/maplevels/1/blueprint -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "levelid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: levelid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/maplevels/{levelid}/blueprint/{filename}": { + "get": { + "tags": [ + "core-maplevels" + ], + "summary": "Serve a level's blueprint image. Public, for the same reason as above.", + "description": "Serve a level's blueprint image. Public, for the same reason as above.\n\n**Auth:** jwt-optional (works signed out; the handler decides what shows)\n\n**Params:** path: levelid (int), filename (path)\n\n**Example:**\n```\ncurl http://localhost:5001/api/maplevels/1/blueprint/example.png -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + }, + {} + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "levelid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "filename", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "may contain slashes" + } + } + ] + } + }, + "/api/maplevels/buildings": { + "post": { + "tags": [ + "core-maplevels" + ], + "summary": "UNDOCUMENTED: maplevels.create_building has no docstring.", + "description": "UNDOCUMENTED: maplevels.create_building has no docstring.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/maplevels/buildings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/maplevels/buildings/{buildingid}": { + "patch": { + "tags": [ + "core-maplevels" + ], + "summary": "Rename or reorder a building. Levels move with it; nothing repositions.", + "description": "Rename or reorder a building. Levels move with it; nothing repositions.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: buildingid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PATCH http://localhost:5001/api/maplevels/buildings/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "buildingid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: buildingid (int); body: JSON (fields not yet documented)" + } + } + } + } + }, + "put": { + "tags": [ + "core-maplevels" + ], + "summary": "Rename or reorder a building. Levels move with it; nothing repositions.", + "description": "Rename or reorder a building. Levels move with it; nothing repositions.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: buildingid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PATCH http://localhost:5001/api/maplevels/buildings/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "buildingid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: buildingid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/mappositions/positions": { + "post": { + "tags": [ + "core-mappositions" + ], + "summary": "Set positions for many assets at once.", + "description": "Set positions for many assets at once.\n\n**Auth:** permission:assets.edit\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/mappositions/positions -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/mappositions/snapshots": { + "get": { + "tags": [ + "core-mappositions" + ], + "summary": "Snapshots, newest first. Metadata only - the positions are large.", + "description": "Snapshots, newest first. Metadata only - the positions are large.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/mappositions/snapshots -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/mappositions/snapshots/{snapshotid}/restore": { + "post": { + "tags": [ + "core-mappositions" + ], + "summary": "Put the positions in this snapshot back.", + "description": "Put the positions in this snapshot back.\n\n**Auth:** permission:assets.edit\n\n**Params:** path: snapshotid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/mappositions/snapshots/1/restore -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "snapshotid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: snapshotid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/mappositions/transform": { + "post": { + "tags": [ + "core-mappositions" + ], + "summary": "Move every placed marker on a level by a transform read off landmarks.", + "description": "Move every placed marker on a level by a transform read off landmarks.\n\n**Auth:** permission:assets.edit\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/mappositions/transform -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/mappositions/verify": { + "post": { + "tags": [ + "core-mappositions" + ], + "summary": "Mark markers as reviewed against the current drawing without moving them.", + "description": "Mark markers as reviewed against the current drawing without moving them.\n\n**Auth:** permission:assets.edit\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/mappositions/verify -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/models": { + "get": { + "tags": [ + "core-models" + ], + "summary": "List all vendor catalog models.", + "description": "List all vendor catalog models.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/models -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "core-models" + ], + "summary": "Create a new model.", + "description": "Create a new model.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/models -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/models/{model_id}": { + "delete": { + "tags": [ + "core-models" + ], + "summary": "Delete (deactivate) a model.", + "description": "Delete (deactivate) a model.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: model_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/models/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "model_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "core-models" + ], + "summary": "Get a single model.", + "description": "Get a single model.\n\n**Auth:** permission:assets.view\n\n**Params:** path: model_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/models/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "model_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-models" + ], + "summary": "Update a model.", + "description": "Update a model.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: model_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/models/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "model_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: model_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/models/{model_id}/image": { + "delete": { + "tags": [ + "core-models" + ], + "summary": "Clear a model image and delete the uploaded file if we own it.", + "description": "Clear a model image and delete the uploaded file if we own it.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: model_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/models/1/image -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "model_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "post": { + "tags": [ + "core-models" + ], + "summary": "Upload (or replace) the photo for a model.", + "description": "Upload (or replace) the photo for a model.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: model_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/models/1/image -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "model_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: model_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/models/image/{filename}": { + "get": { + "tags": [ + "core-models" + ], + "summary": "Serve an uploaded model image (public - asset detail pages read it).", + "description": "Serve an uploaded model image (public - asset detail pages read it).\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** path: filename (path)\n\n**Example:**\n```\ncurl http://localhost:5001/api/models/image/example.png\n```", + "security": [], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "filename", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "may contain slashes" + } + } + ] + } + }, + "/api/modeltypes": { + "get": { + "tags": [ + "core-modeltypes" + ], + "summary": "List all model types with optional filtering.", + "description": "List all model types with optional filtering.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/modeltypes -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "core-modeltypes" + ], + "summary": "Create a new model type.", + "description": "Create a new model type.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/modeltypes -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/modeltypes/{type_id}": { + "delete": { + "tags": [ + "core-modeltypes" + ], + "summary": "Delete (deactivate) a model type.", + "description": "Delete (deactivate) a model type.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/modeltypes/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "core-modeltypes" + ], + "summary": "Get a single model type.", + "description": "Get a single model type.\n\n**Auth:** permission:assets.view\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/modeltypes/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-modeltypes" + ], + "summary": "Update a model type.", + "description": "Update a model type.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: type_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/modeltypes/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: type_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/operatingsystems": { + "get": { + "tags": [ + "core-operatingsystems" + ], + "summary": "List all operating systems.", + "description": "List all operating systems.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/operatingsystems -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "core-operatingsystems" + ], + "summary": "Create a new operating system.", + "description": "Create a new operating system.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/operatingsystems -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/operatingsystems/{os_id}": { + "delete": { + "tags": [ + "core-operatingsystems" + ], + "summary": "Delete (deactivate) an operating system.", + "description": "Delete (deactivate) an operating system.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: os_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/operatingsystems/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "os_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "core-operatingsystems" + ], + "summary": "Get a single operating system.", + "description": "Get a single operating system.\n\n**Auth:** permission:assets.view\n\n**Params:** path: os_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/operatingsystems/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "os_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-operatingsystems" + ], + "summary": "Update an operating system.", + "description": "Update an operating system.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: os_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/operatingsystems/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "os_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: os_id (int); body: JSON (fields not yet documented)" } } } @@ -3541,12 +6812,11 @@ "/api/plugins": { "get": { "tags": [ - "core-platform" + "core-plugins" ], - "summary": "List all discovered plugins (enabled or not) with metadata plus the framework contract version", - "description": "List all discovered plugins (enabled or not) with metadata plus the framework contract version\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/plugins\n```", + "summary": "List all discovered plugins (enabled or not) with their metadata and the framework contract version.", + "description": "List all discovered plugins (enabled or not) with their metadata and the framework contract version.\n\n**Auth:** permission:settings.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/plugins -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -3567,72 +6837,12 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } - } - } - } - }, - "/api/plugins/enabled": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Flat sorted array of enabled plugin names (registry read, no DB); deliberately anonymous so kiosk routes can gate...", - "description": "Flat sorted array of enabled plugin names (registry read, no DB); deliberately anonymous so kiosk routes can gate plugin-owned frontend routes\n\n**Auth:** none (jwt-optional decorator, no claims used)\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/plugins/enabled\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/plugins/settings-routes": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Settings routes contributed by enabled plugins for dynamic UI extension", - "description": "Returns settings routes from all enabled plugins via get_settings_routes() hook. Frontend router merges these to add plugin tabs/pages to settings hubs like Model Catalog.\n\n**Auth:** none (jwt-optional decorator, deliberately anonymous so unauthenticated pages can load plugin routes)\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/plugins/settings-routes\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } + "401": { + "description": "Missing or invalid credentials." }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } + "403": { + "description": "Authenticated, but not permitted." } } } @@ -3640,10 +6850,10 @@ "/api/plugins/{name}": { "put": { "tags": [ - "core-platform" + "core-plugins" ], - "summary": "Enable or disable a plugin (route changes need an app restart); 409 when unknown or a dependency conflict refuses the...", - "description": "Enable or disable a plugin (route changes need an app restart); 409 when unknown or a dependency conflict refuses the change\n\n**Auth:** jwt + role:admin\n\n**Params:** body: enabled (bool, required)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/plugins/warranty -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"enabled\":false}'\n```", + "summary": "Enable or disable a plugin. Takes effect on the next app restart for route/navigation changes.", + "description": "Enable or disable a plugin. Takes effect on the next app restart for route/navigation changes.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: name (string); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/plugins/x -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -3693,22 +6903,54 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: enabled (bool, required)" + "description": "path: name (string); body: JSON (fields not yet documented)" } } } } } }, - "/api/pluginui/settings-cards": { + "/api/plugins/enabled": { "get": { "tags": [ - "core-platform" + "core-plugins" ], - "summary": "Merge enabled plugins' settings-catalog cards (get_settings_cards hook) sorted by position for the settings...", - "description": "Merge enabled plugins' settings-catalog cards (get_settings_cards hook) sorted by position for the settings rail/overview\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/pluginui/settings-cards\n```", + "summary": "Return just the names of enabled plugins as a flat array.", + "description": "Return just the names of enabled plugins as a flat array.\n\n**Auth:** jwt-optional (works signed out; the handler decides what shows)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/plugins/enabled -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + }, + {} + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + } + } + }, + "/api/plugins/settings-routes": { + "get": { + "tags": [ + "core-plugins" + ], + "summary": "Return settings routes contributed by all enabled plugins.", + "description": "Return settings routes contributed by all enabled plugins.\n\n**Auth:** permission:settings.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/plugins/settings-routes -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -3729,6 +6971,12 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." } } } @@ -3736,12 +6984,11 @@ "/api/pluginui/asset-panels": { "get": { "tags": [ - "core-platform" + "core-pluginui" ], - "summary": "Asset-detail panels from enabled plugins (get_asset_panels hook) matching one asset's type ('*' wildcard supported)...", - "description": "Asset-detail panels from enabled plugins (get_asset_panels hook) matching one asset's type ('*' wildcard supported), sorted by position\n\n**Auth:** jwt-optional\n\n**Params:** assetid (int, required); 400 without it, 404 if asset missing\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/pluginui/asset-panels?assetid=42'\n```", + "summary": "Return the asset-detail panels matching one asset's type (get_asset_panels).", + "description": "Return the asset-detail panels matching one asset's type (get_asset_panels).\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/pluginui/asset-panels -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -3762,39 +7009,12 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } - } - } - } - }, - "/api/pluginui/map-overlays": { - "get": { - "tags": [ - "core-platform" - ], - "summary": "Merge enabled plugins' map overlay declarations (get_map_overlays hook), sorted by position", - "description": "Merge enabled plugins' map overlay declarations (get_map_overlays hook), sorted by position\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/pluginui/map-overlays\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." } } } @@ -3802,12 +7022,11 @@ "/api/pluginui/asset-presentation": { "get": { "tags": [ - "core-platform" + "core-pluginui" ], - "summary": "Merge enabled plugins' asset-type presentation entries (icon + detail route per type) used by search rows and...", - "description": "Merge enabled plugins' asset-type presentation entries (icon + detail route per type) used by search rows and cross-links\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/pluginui/asset-presentation\n```", + "summary": "Merge enabled plugins' asset-type presentation entries (hook).", + "description": "Merge enabled plugins' asset-type presentation entries (hook).\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/pluginui/asset-presentation -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -3828,18 +7047,935 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." } } } }, - "/api/employees/search": { + "/api/pluginui/asset-type-descriptors": { "get": { "tags": [ - "plugin-employees" + "core-pluginui" + ], + "summary": "Merge enabled plugins' asset-type subtype descriptors.", + "description": "Merge enabled plugins' asset-type subtype descriptors.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/pluginui/asset-type-descriptors -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/pluginui/core-type-descriptors": { + "get": { + "tags": [ + "core-pluginui" + ], + "summary": "Return type descriptors for core-owned non-asset types.", + "description": "Return type descriptors for core-owned non-asset types.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/pluginui/core-type-descriptors -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/pluginui/map-overlays": { + "get": { + "tags": [ + "core-pluginui" + ], + "summary": "Merge enabled plugins' map overlay declarations (get_map_overlays hook).", + "description": "Merge enabled plugins' map overlay declarations (get_map_overlays hook).\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/pluginui/map-overlays -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/pluginui/settings-cards": { + "get": { + "tags": [ + "core-pluginui" + ], + "summary": "Merge enabled plugins' settings-catalog cards (get_settings_cards hook).", + "description": "Merge enabled plugins' settings-catalog cards (get_settings_cards hook).\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/pluginui/settings-cards -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/reports": { + "get": { + "tags": [ + "core-reports" + ], + "summary": "List all available reports.", + "description": "List all available reports.\n\n**Auth:** permission:reports.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/reports -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/reports/asset-inventory": { + "get": { + "tags": [ + "core-reports" + ], + "summary": "Report: Complete asset inventory summary.", + "description": "Report: Complete asset inventory summary.\n\n**Auth:** permission:reports.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/reports/asset-inventory -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/reports/assets-by-status": { + "get": { + "tags": [ + "core-reports" + ], + "summary": "Report: Asset count grouped by status.", + "description": "Report: Asset count grouped by status.\n\n**Auth:** permission:reports.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/reports/assets-by-status -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/reports/email": { + "post": { + "tags": [ + "core-reports" + ], + "summary": "Email a report's data as an HTML table (on-demand delivery).", + "description": "Email a report's data as an HTML table (on-demand delivery).\n\n**Auth:** permission:reports.export\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/reports/email -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/reports/kb-popularity": { + "get": { + "tags": [ + "core-reports" + ], + "summary": "Report: Most clicked knowledge base articles.", + "description": "Report: Most clicked knowledge base articles.\n\n**Auth:** permission:reports.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/reports/kb-popularity -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/reports/machines-by-type": { + "get": { + "tags": [ + "core-reports" + ], + "summary": "Report: Machine count grouped by machine type.", + "description": "Report: Machine count grouped by machine type.\n\n**Auth:** permission:reports.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/reports/machines-by-type -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/reports/pc-relationships": { + "get": { + "tags": [ + "core-reports" + ], + "summary": "Report: PCs with relationships to shop floor machines.", + "description": "Report: PCs with relationships to shop floor machines.\n\n**Auth:** permission:reports.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/reports/pc-relationships -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/reports/software-compliance": { + "get": { + "tags": [ + "core-reports" + ], + "summary": "Report: Required applications vs installed (per PC).", + "description": "Report: Required applications vs installed (per PC).\n\n**Auth:** permission:reports.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/reports/software-compliance -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/scripts": { + "get": { + "tags": [ + "core-scripts" + ], + "summary": "List scripts (active by default).", + "description": "List scripts (active by default).\n\n**Auth:** permission:endpoints.runscript\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/scripts -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "core-scripts" + ], + "summary": "Create a script catalog entry (body uploaded separately).", + "description": "Create a script catalog entry (body uploaded separately).\n\n**Auth:** permission:endpoints.runscript\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/scripts -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/scripts/{script_id}": { + "delete": { + "tags": [ + "core-scripts" + ], + "summary": "UNDOCUMENTED: scripts.delete_script has no docstring.", + "description": "UNDOCUMENTED: scripts.delete_script has no docstring.\n\n**Auth:** permission:endpoints.runscript\n\n**Params:** path: script_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/scripts/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "script_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "core-scripts" + ], + "summary": "UNDOCUMENTED: scripts.get_script has no docstring.", + "description": "UNDOCUMENTED: scripts.get_script has no docstring.\n\n**Auth:** permission:endpoints.runscript\n\n**Params:** path: script_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/scripts/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "script_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-scripts" + ], + "summary": "UNDOCUMENTED: scripts.update_script has no docstring.", + "description": "UNDOCUMENTED: scripts.update_script has no docstring.\n\n**Auth:** permission:endpoints.runscript\n\n**Params:** path: script_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/scripts/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "script_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: script_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/scripts/{script_id}/body": { + "post": { + "tags": [ + "core-scripts" + ], + "summary": "Upload (or replace) the .ps1 body for a script.", + "description": "Upload (or replace) the .ps1 body for a script.\n\n**Auth:** permission:endpoints.runscript\n\n**Params:** path: script_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/scripts/1/body -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "script_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: script_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/scripts/body/{filename}": { + "get": { + "tags": [ + "core-scripts" + ], + "summary": "Operator-facing script body download (JWT/RBAC).", + "description": "Operator-facing script body download (JWT/RBAC).\n\n**Auth:** permission:endpoints.runscript\n\n**Params:** path: filename (path)\n\n**Example:**\n```\ncurl http://localhost:5001/api/scripts/body/example.png -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "filename", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "may contain slashes" + } + } + ] + } + }, + "/api/search": { + "get": { + "tags": [ + "core-search" + ], + "summary": "Global search across multiple entity types.", + "description": "Global search across multiple entity types.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/search -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/search/suggest": { + "get": { + "tags": [ + "core-search" + ], + "summary": "A better spelling for a query that found nothing, or null.", + "description": "A better spelling for a query that found nothing, or null.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/search/suggest -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/settings": { + "get": { + "tags": [ + "core-settings" + ], + "summary": "List all settings, optionally filtered by category.", + "description": "List all settings, optionally filtered by category.\n\n**Auth:** jwt-optional (works signed out; the handler decides what shows)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/settings -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + }, + {} ], - "summary": "Search employees by first name, last name, or SSO substring (self-hosted table or external HR DB depending on...", - "description": "Search employees by first name, last name, or SSO substring (self-hosted table or external HR DB depending on employee_directory_mode setting).\n\n**Auth:** none\n\n**Params:** q (query string, min 2 chars, required), limit (max results, default 10, capped 50)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/employees/search?q=smith&limit=5'\n```", - "security": [], "responses": { "200": { "description": "Success. Body is the success_response envelope: {status, data, meta}.", @@ -3858,16 +7994,69 @@ } } } + }, + "post": { + "tags": [ + "core-settings" + ], + "summary": "Create a new setting (admin only).", + "description": "Create a new setting (admin only).\n\n**Auth:** permission:settings.edit\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/settings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } } }, - "/api/employees/lookup/{sso}": { + "/api/settings/{key}": { "get": { "tags": [ - "plugin-employees" + "core-settings" + ], + "summary": "Get a single setting by key.", + "description": "Get a single setting by key.\n\n**Auth:** jwt-optional (works signed out; the handler decides what shows)\n\n**Params:** path: key (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/settings/x -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + }, + {} ], - "summary": "Look up a single employee by numeric SSO; returns directory fields plus resolved photourl.", - "description": "Look up a single employee by numeric SSO; returns directory fields plus resolved photourl.\n\n**Auth:** none\n\n**Params:** sso (path, numeric)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/employees/lookup/210009518'\n```", - "security": [], "responses": { "200": { "description": "Success. Body is the success_response envelope: {status, data, meta}.", @@ -3891,7 +8080,245 @@ }, "parameters": [ { - "name": "sso", + "name": "key", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "put": { + "tags": [ + "core-settings" + ], + "summary": "Update a setting value.", + "description": "Update a setting value.\n\n**Auth:** permission:settings.edit\n\n**Params:** path: key (string); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/settings/x -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "key", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: key (string); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/settings/asset-import/import": { + "post": { + "tags": [ + "core-settings" + ], + "summary": "Validate uploaded CSVs and, when commit=true, load them.", + "description": "Validate uploaded CSVs and, when commit=true, load them.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/settings/asset-import/import -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/settings/asset-import/script": { + "get": { + "tags": [ + "core-settings" + ], + "summary": "Download Collect-ShopDB.ps1 with this site's CSV path embedded.", + "description": "Download Collect-ShopDB.ps1 with this site's CSV path embedded.\n\n**Auth:** jwt + role:admin\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/settings/asset-import/script -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/settings/asset-import/templates": { + "get": { + "tags": [ + "core-settings" + ], + "summary": "List the importable CSV tables in the order they must be uploaded.", + "description": "List the importable CSV tables in the order they must be uploaded.\n\n**Auth:** jwt + role:admin\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/settings/asset-import/templates -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/settings/asset-import/templates/{tablename}": { + "get": { + "tags": [ + "core-settings" + ], + "summary": "Download a generated CSV template for a single importable table.", + "description": "Download a generated CSV template for a single importable table.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: tablename (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/settings/asset-import/templates/x -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "tablename", "in": "path", "required": true, "schema": { @@ -3901,14 +8328,18 @@ ] } }, - "/api/employees/lookup": { + "/api/settings/asset-import/winc/batches": { "get": { "tags": [ - "plugin-employees" + "core-settings" + ], + "summary": "List applied/undone WINC import batches, newest first (for the undo UI).", + "description": "List applied/undone WINC import batches, newest first (for the undo UI).\n\n**Auth:** jwt + role:admin\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/settings/asset-import/winc/batches -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } ], - "summary": "Bulk lookup of multiple employees by SSO list; returns employees array plus a joined names string.", - "description": "Bulk lookup of multiple employees by SSO list; returns employees array plus a joined names string.\n\n**Auth:** none\n\n**Params:** sso (query, comma-separated numeric SSOs, at least one required)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/employees/lookup?sso=210009518,210001234'\n```", - "security": [], "responses": { "200": { "description": "Success. Body is the success_response envelope: {status, data, meta}.", @@ -3925,372 +8356,223 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." } } } }, - "/api/employees/directory": { + "/api/settings/asset-import/winc/commit": { + "post": { + "tags": [ + "core-settings" + ], + "summary": "Apply a WINC plan reversibly. Requires acknowledgment for needs-create.", + "description": "Apply a WINC plan reversibly. Requires acknowledgment for needs-create.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/settings/asset-import/winc/commit -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/settings/asset-import/winc/preview": { + "post": { + "tags": [ + "core-settings" + ], + "summary": "Read-only WINC analysis: report what a commit would do. Writes nothing.", + "description": "Read-only WINC analysis: report what a commit would do. Writes nothing.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/settings/asset-import/winc/preview -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/settings/asset-import/winc/undo": { + "post": { + "tags": [ + "core-settings" + ], + "summary": "Reverse an applied WINC import batch by id. Conservative and reversible.", + "description": "Reverse an applied WINC import batch by id. Conservative and reversible.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/settings/asset-import/winc/undo -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/settings/branding-logo": { + "post": { + "tags": [ + "core-settings" + ], + "summary": "Upload a branding logo image and point the matching setting at it.", + "description": "Upload a branding logo image and point the matching setting at it.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/settings/branding-logo -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/settings/branding/{filename}": { "get": { "tags": [ - "plugin-employees" + "core-settings" ], - "summary": "List the full self-hosted directory for the management page; 400 when directory mode is external.", - "description": "List the full self-hosted directory for the management page; 400 when directory mode is external.\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/employees/directory'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "plugin-employees" - ], - "summary": "Create a self-hosted directory employee; 409 if SSO exists, 400 in external mode.", - "description": "Create a self-hosted directory employee; 409 if SSO exists, 400 in external mode.\n\n**Auth:** jwt + require_role admin\n\n**Params:** JSON body: sso (numeric, required), firstname, lastname (required), team, role, picture (also accepts external-style keys SSO/First_Name/Last_Name/Team/Role/Picture)\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"sso\":210009518,\"firstname\":\"Jane\",\"lastname\":\"Doe\",\"team\":\"CNC\",\"role\":\"Machinist\"}' 'http://localhost:5001/api/employees/directory'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "JSON body: sso (numeric, required), firstname, lastname (required), team, role, picture (also accepts external-style keys SSO/First_Name/Last_Name/Team/Role/Picture)" - } - } - } - } - } - }, - "/api/employees/directory/{sso}": { - "put": { - "tags": [ - "plugin-employees" - ], - "summary": "Update a self-hosted directory employee's name/team/role/picture; 404 if missing, 400 in external mode.", - "description": "Update a self-hosted directory employee's name/team/role/picture; 404 if missing, 400 in external mode.\n\n**Auth:** jwt + require_role admin\n\n**Params:** sso (path); JSON body: firstname, lastname, team, role, picture (external-style keys also accepted; team/role/picture can be cleared)\n\n**Example:**\n```\ncurl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"team\":\"Quality\"}' 'http://localhost:5001/api/employees/directory/210009518'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "sso", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "sso (path); JSON body: firstname, lastname, team, role, picture (external-style keys also accepted; team/role/picture can be cleared)" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-employees" - ], - "summary": "Delete a self-hosted directory employee; 404 if missing, 400 in external mode.", - "description": "Delete a self-hosted directory employee; 404 if missing, 400 in external mode.\n\n**Auth:** jwt + require_role admin\n\n**Params:** sso (path)\n\n**Example:**\n```\ncurl -X DELETE -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/employees/directory/210009518'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "sso", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - } - }, - "/api/employees/directory/import": { - "post": { - "tags": [ - "plugin-employees" - ], - "summary": "Bulk upsert the self-hosted directory from CSV (headers SSO,First_Name,Last_Name,Team,Role,Picture case-insensitive...", - "description": "Bulk upsert the self-hosted directory from CSV (headers SSO,First_Name,Last_Name,Team,Role,Picture case-insensitive; plain firstname/lastname also accepted); returns added/updated/skipped counts.\n\n**Auth:** jwt + require_role admin\n\n**Params:** multipart file= OR JSON body {\"csv\": \"...\"}; rows missing numeric sso or names are skipped\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -F 'file=@employees.csv' 'http://localhost:5001/api/employees/directory/import'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "multipart file= OR JSON body {\"csv\": \"...\"}; rows missing numeric sso or names are skipped" - } - } - } - } - } - }, - "/api/employees/{sso}/photo": { - "post": { - "tags": [ - "plugin-employees" - ], - "summary": "Upload or replace a self-hosted employee's photo (saved as photo- in instance employeephotos dir; old file...", - "description": "Upload or replace a self-hosted employee's photo (saved as photo- in instance employeephotos dir; old file wiped even on extension change); 409 in external mode, 404 if employee missing.\n\n**Auth:** jwt + require_role admin\n\n**Params:** sso (path); multipart/form-data file=, extensions .png/.jpg/.jpeg/.gif/.webp only\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -F 'file=@jane.jpg' 'http://localhost:5001/api/employees/210009518/photo'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "sso", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "sso (path); multipart/form-data file=, extensions .png/.jpg/.jpeg/.gif/.webp only" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-employees" - ], - "summary": "Clear a self-hosted employee's photo record and delete the uploaded file; 409 in external mode, 404 if employee missing.", - "description": "Clear a self-hosted employee's photo record and delete the uploaded file; 409 in external mode, 404 if employee missing.\n\n**Auth:** jwt + require_role admin\n\n**Params:** sso (path)\n\n**Example:**\n```\ncurl -X DELETE -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/employees/210009518/photo'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "sso", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - } - }, - "/api/employees/photo/{filename}": { - "get": { - "tags": [ - "plugin-employees" - ], - "summary": "Serve an uploaded employee photo file from the instance employeephotos dir (public so kiosk recognition cards can read...", - "description": "Serve an uploaded employee photo file from the instance employeephotos dir (public so kiosk recognition cards can read it).\n\n**Auth:** none\n\n**Params:** filename (path, e.g. photo-210009518.jpg)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/employees/photo/photo-210009518.jpg'\n```", + "summary": "Serve an uploaded branding logo (public - kiosks/print pages read it).", + "description": "Serve an uploaded branding logo (public - kiosks/print pages read it).\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** path: filename (path)\n\n**Example:**\n```\ncurl http://localhost:5001/api/settings/branding/example.png\n```", "security": [], "responses": { "200": { @@ -4326,95 +8608,13 @@ ] } }, - "/api/employees/resolve/{sso}": { - "get": { - "tags": [ - "plugin-employees" - ], - "summary": "Resolve one SSO to a name and photo, and say HOW it resolved or why it did not. Built because the failure is otherwise...", - "description": "Resolve one SSO to a name and photo, and say HOW it resolved or why it did not. Built because the failure is otherwise invisible: the shop-floor board resolves names through functions that returned None on any miss, so a directory problem and a typo looked identical\n\n**Auth:** jwt\n\n**Params:** sso in path\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/employees/resolve/123456789\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "sso", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/knowledgebase": { - "get": { - "tags": [ - "plugin-knowledgebase" - ], - "summary": "List active KB articles with search, topic filter, natural-key lookup, sorting, and pagination; each item includes its...", - "description": "List active KB articles with search, topic filter, natural-key lookup, sorting, and pagination; each item includes its linked application (appid/appname) or null.\n\n**Auth:** jwt-optional\n\n**Params:** query: page, per_page, search (ILIKE on shortdescription/keywords/application name), appid (int filter), linkurl (exact natural-key match), shortdescription (exact match), sort (clicks|topic|description|lastupdated, default clicks), order (asc|desc, default desc)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/knowledgebase?search=printer&sort=clicks&order=desc&page=1&per_page=20'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, + "/api/settings/map-blueprint": { "post": { "tags": [ - "plugin-knowledgebase" + "core-settings" ], - "summary": "Create a new KB article; validates required fields and that appid (if given) exists; honors X-Import-Mode timestamp...", - "description": "Create a new KB article; validates required fields and that appid (if given) exists; honors X-Import-Mode timestamp preservation via apply_import_timestamps; returns 201.\n\n**Auth:** permission:kb.create (jwt required)\n\n**Params:** body JSON: shortdescription (required), linkurl (required), appid (optional int, must exist), keywords (optional); import-mode may pass timestamp fields\n\n**Example:**\n```\ncurl -X POST 'http://localhost:5001/api/knowledgebase' -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"shortdescription\":\"VPN setup guide\",\"linkurl\":\"https://wiki.example.com/vpn\",\"appid\":3,\"keywords\":\"vpn,remote\"}'\n```", + "summary": "Upload a floor-map blueprint image and point the setting at it.", + "description": "Upload a floor-map blueprint image and point the setting at it.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/settings/map-blueprint -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -4451,2410 +8651,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body JSON: shortdescription (required), linkurl (required), appid (optional int, must exist), keywords (optional); import-mode may pass timestamp fields" + "description": "body: JSON (fields not yet documented)" } } } } } }, - "/api/knowledgebase/stats": { + "/api/settings/map-blueprint/{filename}": { "get": { "tags": [ - "plugin-knowledgebase" + "core-settings" ], - "summary": "Return aggregate stats for active articles: totalclicks (sum of clicks) and totalarticles (count).", - "description": "Return aggregate stats for active articles: totalclicks (sum of clicks) and totalarticles (count).\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/knowledgebase/stats'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/knowledgebase/{link_id}": { - "get": { - "tags": [ - "plugin-knowledgebase" - ], - "summary": "Fetch a single active article by id with its application (appid/appname) or null; 404 if missing or inactive.", - "description": "Fetch a single active article by id with its application (appid/appname) or null; 404 if missing or inactive.\n\n**Auth:** jwt-optional\n\n**Params:** path: link_id (int)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/knowledgebase/42'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "link_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - }, - "put": { - "tags": [ - "plugin-knowledgebase" - ], - "summary": "Update an article's shortdescription, linkurl, appid, keywords, and/or isactive; validates appid if changed; honors...", - "description": "Update an article's shortdescription, linkurl, appid, keywords, and/or isactive; validates appid if changed; honors import timestamps; 404 if article missing.\n\n**Auth:** permission:kb.edit (jwt required)\n\n**Params:** path: link_id (int); body JSON: any of shortdescription, linkurl, appid, keywords, isactive\n\n**Example:**\n```\ncurl -X PUT 'http://localhost:5001/api/knowledgebase/42' -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"keywords\":\"vpn,zscaler\",\"isactive\":true}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "link_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: link_id (int); body JSON: any of shortdescription, linkurl, appid, keywords, isactive" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-knowledgebase" - ], - "summary": "Soft-delete an article by setting isactive=false (row is retained); 404 if article missing.", - "description": "Soft-delete an article by setting isactive=false (row is retained); 404 if article missing.\n\n**Auth:** permission:kb.delete (jwt required)\n\n**Params:** path: link_id (int)\n\n**Example:**\n```\ncurl -X DELETE 'http://localhost:5001/api/knowledgebase/42' -H \"Authorization: Bearer $TOKEN\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "link_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - } - }, - "/api/knowledgebase/{link_id}/click": { - "post": { - "tags": [ - "plugin-knowledgebase" - ], - "summary": "Increment the article's click counter and return the target linkurl plus new click count (used for redirect tracking)...", - "description": "Increment the article's click counter and return the target linkurl plus new click count (used for redirect tracking); 404 if missing or inactive.\n\n**Auth:** jwt-optional\n\n**Params:** path: link_id (int); no body\n\n**Example:**\n```\ncurl -X POST 'http://localhost:5001/api/knowledgebase/42/click'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "link_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: link_id (int); no body" - } - } - } - } - } - }, - "/api/assets/types": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "List asset types (paginated, active-only by default)", - "description": "List asset types (paginated, active-only by default)\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active=false to include inactive\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/types?active=false\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "core-catalog" - ], - "summary": "Create asset type (409 on duplicate name)", - "description": "Create asset type (409 on duplicate name)\n\n**Auth:** permission:assets.create\n\n**Params:** body: assettype (req), pluginname, tablename, description, icon, color\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/assets/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"assettype\":\"Robot\",\"icon\":\"mdi-robot\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: assettype (req), pluginname, tablename, description, icon, color" - } - } - } - } - } - }, - "/api/assets/types/{type_id}": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "Get one asset type", - "description": "Get one asset type\n\n**Auth:** jwt-optional\n\n**Params:** path: type_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/types/1\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "core-catalog" - ], - "summary": "Update asset type display fields only (name/plugin/table are structural, not editable)", - "description": "Update asset type display fields only (name/plugin/table are structural, not editable)\n\n**Auth:** permission:assets.edit\n\n**Params:** body: description, icon, color, isactive\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/assets/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"color\":\"#ff0000\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: description, icon, color, isactive" - } - } - } - } - } - }, - "/api/assets/statuses": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "List asset statuses (paginated, active-only by default)", - "description": "List asset statuses (paginated, active-only by default)\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active=false\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/statuses\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "core-catalog" - ], - "summary": "Create asset status (409 on duplicate)", - "description": "Create asset status (409 on duplicate)\n\n**Auth:** permission:assets.create\n\n**Params:** body: status (req), description, color\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/assets/statuses -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"status\":\"In Repair\",\"color\":\"#f90\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: status (req), description, color" - } - } - } - } - } - }, - "/api/assets/statuses/{status_id}": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "Get one asset status", - "description": "Get one asset status\n\n**Auth:** jwt-optional\n\n**Params:** path: status_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/statuses/1\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "status_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "core-catalog" - ], - "summary": "Update asset status (rename conflict-checked)", - "description": "Update asset status (rename conflict-checked)\n\n**Auth:** permission:assets.edit\n\n**Params:** body: status, description, color, isactive\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/assets/statuses/2 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"isactive\":false}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "status_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: status, description, color, isactive" - } - } - } - } - }, - "delete": { - "tags": [ - "core-catalog" - ], - "summary": "Hard-delete asset status; 409 if any asset still uses it", - "description": "Hard-delete asset status; 409 if any asset still uses it\n\n**Auth:** permission:assets.delete\n\n**Params:** path: status_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/assets/statuses/9 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "status_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/assets/relationshiptypes": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "List relationship types incl. read-only propagatesthrough rails", - "description": "List relationship types incl. read-only propagatesthrough rails\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/relationshiptypes\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "core-catalog" - ], - "summary": "Create relationship type (409 on duplicate)", - "description": "Create relationship type (409 on duplicate)\n\n**Auth:** permission:assets.create\n\n**Params:** body: relationshiptype (req), description, color, isdirectional (default true)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/assets/relationshiptypes -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"relationshiptype\":\"controls\",\"isdirectional\":true}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: relationshiptype (req), description, color, isdirectional (default true)" - } - } - } - } - } - }, - "/api/assets/relationshiptypes/{type_id}": { - "put": { - "tags": [ - "core-catalog" - ], - "summary": "Update relationship type (rename conflict-checked)", - "description": "Update relationship type (rename conflict-checked)\n\n**Auth:** permission:assets.edit\n\n**Params:** body: relationshiptype, description, color, isdirectional\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/assets/relationshiptypes/3 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"color\":\"#00f\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: relationshiptype, description, color, isdirectional" - } - } - } - } - }, - "delete": { - "tags": [ - "core-catalog" - ], - "summary": "Hard-delete relationship type; 409 while relationships use it", - "description": "Hard-delete relationship type; 409 while relationships use it\n\n**Auth:** permission:assets.delete\n\n**Params:** path: type_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/assets/relationshiptypes/3 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/assets": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "List assets with filtering, search, sorting, pagination", - "description": "List assets with filtering, search, sorting, pagination\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active, search (assetnumber/name/serialnumber ilike), type (name), typeid|type_id, statusid|status_id, locationid|location_id, businessunitid|businessunit_id, sort (assetnumber|name|createddate|modifieddate), dir (asc|desc), include_type_data\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/assets?type=machine&search=205&include_type_data=true'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "core-catalog" - ], - "summary": "Create asset (duplicate assetnumber 409, assettypeid validated); honors X-Import-Mode timestamps", - "description": "Create asset (duplicate assetnumber 409, assettypeid validated); honors X-Import-Mode timestamps\n\n**Auth:** permission:assets.create\n\n**Params:** body: assetnumber (req), assettypeid (req), name, serialnumber, statusid (default 1), locationid, businessunitid, mapx, mapy, notes\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/assets -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"assetnumber\":\"0205\",\"assettypeid\":1,\"name\":\"Grinder 5\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: assetnumber (req), assettypeid (req), name, serialnumber, statusid (default 1), locationid, businessunitid, mapx, mapy, notes" - } - } - } - } - } - }, - "/api/assets/{asset_id}": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "Get one asset with full details", - "description": "Get one asset with full details\n\n**Auth:** jwt-optional\n\n**Params:** include_type_data (default true)\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/42\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "core-catalog" - ], - "summary": "Update asset (allowed fields incl. isactive); honors X-Import-Mode", - "description": "Update asset (allowed fields incl. isactive); honors X-Import-Mode\n\n**Auth:** permission:assets.edit\n\n**Params:** body: assetnumber, name, serialnumber, assettypeid, statusid, locationid, businessunitid, mapx, mapy, notes, isactive\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/assets/42 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"locationid\":3}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: assetnumber, name, serialnumber, assettypeid, statusid, locationid, businessunitid, mapx, mapy, notes, isactive" - } - } - } - } - }, - "delete": { - "tags": [ - "core-catalog" - ], - "summary": "Soft-delete asset (isactive=false)", - "description": "Soft-delete asset (isactive=false)\n\n**Auth:** permission:assets.delete\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/assets/42 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/assets/lookup/{assetnumber}": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "Look up an active asset by asset number (returns full type data)", - "description": "Look up an active asset by asset number (returns full type data)\n\n**Auth:** jwt-optional\n\n**Params:** path: assetnumber (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/lookup/0205\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "assetnumber", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/assets/{asset_id}/relationships": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "Get outgoing + incoming active relationships for an asset with partner asset dicts", - "description": "Get outgoing + incoming active relationships for an asset with partner asset dicts\n\n**Auth:** jwt-optional\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/42/relationships\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/assets/relationships": { - "post": { - "tags": [ - "core-catalog" - ], - "summary": "Create relationship, then fan out across symmetric propagation rails (Dualpath); response carries propagated...", - "description": "Create relationship, then fan out across symmetric propagation rails (Dualpath); response carries propagated + propagatedcount\n\n**Auth:** permission:assets.create\n\n**Params:** body: sourceassetid (req), targetassetid (req), relationshiptypeid (req), notes; X-Import-Mode honored\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/assets/relationships -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"sourceassetid\":10,\"targetassetid\":42,\"relationshiptypeid\":1}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: sourceassetid (req), targetassetid (req), relationshiptypeid (req), notes; X-Import-Mode honored" - } - } - } - } - } - }, - "/api/assets/relationships/{rel_id}": { - "delete": { - "tags": [ - "core-catalog" - ], - "summary": "Soft-delete one relationship row (no cascade to propagated partner rows)", - "description": "Soft-delete one relationship row (no cascade to propagated partner rows)\n\n**Auth:** permission:assets.delete\n\n**Params:** path: rel_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/assets/relationships/7 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "rel_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/assets/map": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "Unified floor-map payload: all mapped assets (with type data, primary IP, dualpath collapse) plus filter option lists", - "description": "Unified floor-map payload: all mapped assets (with type data, primary IP, dualpath collapse) plus filter option lists\n\n**Auth:** jwt-optional\n\n**Params:** assettype (name), subtype (id, per-type), businessunitid, statusid, locationid, search\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/assets/map?assettype=machine&statusid=1'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/assets/{asset_id}/communications": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "List active communications (IPs etc.) for an asset with comtype_name", - "description": "List active communications (IPs etc.) for an asset with comtype_name\n\n**Auth:** jwt-optional\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/assets/42/communications\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/locations/types": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "List location types", - "description": "List location types\n\n**Auth:** jwt-optional\n\n**Params:** active=false includes inactive\n\n**Example:**\n```\ncurl http://localhost:5001/api/locations/types\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "core-catalog" - ], - "summary": "Create location type; reactivates a soft-deleted same-name type instead of 409", - "description": "Create location type; reactivates a soft-deleted same-name type instead of 409\n\n**Auth:** admin\n\n**Params:** body: locationtype (req), description, color\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/locations/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"locationtype\":\"Operation\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: locationtype (req), description, color" - } - } - } - } - } - }, - "/api/locations/types/{type_id}": { - "put": { - "tags": [ - "core-catalog" - ], - "summary": "Update location type (rename conflict-checked)", - "description": "Update location type (rename conflict-checked)\n\n**Auth:** admin\n\n**Params:** body: locationtype, description, color, isactive\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/locations/types/2 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"color\":\"#0a0\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: locationtype, description, color, isactive" - } - } - } - } - }, - "delete": { - "tags": [ - "core-catalog" - ], - "summary": "Hard-delete location type; 409 while locations use it", - "description": "Hard-delete location type; 409 while locations use it\n\n**Auth:** admin\n\n**Params:** path: type_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/locations/types/2 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/locations": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "List locations (paginated); exact locationname lookup for idempotent import", - "description": "List locations (paginated); exact locationname lookup for idempotent import\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active, locationname (exact), search (name/building ilike)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/locations?locationname=Building%201'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "core-catalog" - ], - "summary": "Create location (409 on duplicate name); honors X-Import-Mode", - "description": "Create location (409 on duplicate name); honors X-Import-Mode\n\n**Auth:** admin\n\n**Params:** body: locationname (req), building, floor, room, description, locationtypeid, parentlocationid, mapimage, mapwidth, mapheight\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/locations -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"locationname\":\"Cell 12\",\"building\":\"B1\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: locationname (req), building, floor, room, description, locationtypeid, parentlocationid, mapimage, mapwidth, mapheight" - } - } - } - } - } - }, - "/api/locations/{location_id}": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "Get one location", - "description": "Get one location\n\n**Auth:** jwt-optional\n\n**Params:** path: location_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/locations/3\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "location_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "core-catalog" - ], - "summary": "Update location (rename conflict-checked); honors X-Import-Mode", - "description": "Update location (rename conflict-checked); honors X-Import-Mode\n\n**Auth:** admin\n\n**Params:** body: locationname, building, floor, room, description, locationtypeid, parentlocationid, mapimage, mapwidth, mapheight, isactive\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/locations/3 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"room\":\"104\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "location_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: locationname, building, floor, room, description, locationtypeid, parentlocationid, mapimage, mapwidth, mapheight, isactive" - } - } - } - } - }, - "delete": { - "tags": [ - "core-catalog" - ], - "summary": "Soft-delete location", - "description": "Soft-delete location\n\n**Auth:** admin\n\n**Params:** path: location_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/locations/3 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "location_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/vendors": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "List vendors (paginated); exact vendor lookup for idempotent import", - "description": "List vendors (paginated); exact vendor lookup for idempotent import\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active, vendor (exact), search (ilike)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/vendors?search=fanuc'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "core-catalog" - ], - "summary": "Create vendor (409 on duplicate); honors X-Import-Mode", - "description": "Create vendor (409 on duplicate); honors X-Import-Mode\n\n**Auth:** admin\n\n**Params:** body: vendor (req), description, website, supportphone, supportemail, notes\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/vendors -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"vendor\":\"Fanuc\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: vendor (req), description, website, supportphone, supportemail, notes" - } - } - } - } - } - }, - "/api/vendors/{vendor_id}": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "Get one vendor", - "description": "Get one vendor\n\n**Auth:** jwt-optional\n\n**Params:** path: vendor_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/vendors/5\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "vendor_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "core-catalog" - ], - "summary": "Update vendor (rename conflict-checked); honors X-Import-Mode", - "description": "Update vendor (rename conflict-checked); honors X-Import-Mode\n\n**Auth:** admin\n\n**Params:** body: vendor, description, website, supportphone, supportemail, notes, isactive\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/vendors/5 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"website\":\"https://fanuc.com\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "vendor_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: vendor, description, website, supportphone, supportemail, notes, isactive" - } - } - } - } - }, - "delete": { - "tags": [ - "core-catalog" - ], - "summary": "Soft-delete vendor", - "description": "Soft-delete vendor\n\n**Auth:** admin\n\n**Params:** path: vendor_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/vendors/5 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "vendor_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/models": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "List vendor catalog models (paginated) with flattened vendor/modeltype names; exact modelnumber+vendor lookup for import", - "description": "List vendor catalog models (paginated) with flattened vendor/modeltype names; exact modelnumber+vendor lookup for import\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active, vendor (id), modeltype (id), modelnumber (exact), search (ilike)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/models?vendor=5&search=30i'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "core-catalog" - ], - "summary": "Create model (409 on duplicate modelnumber+vendorid); honors X-Import-Mode", - "description": "Create model (409 on duplicate modelnumber+vendorid); honors X-Import-Mode\n\n**Auth:** admin\n\n**Params:** body: modelnumber (req), vendorid, modeltypeid, description, imageurl, documentationurl, notes\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/models -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"modelnumber\":\"R-30iB\",\"vendorid\":5}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: modelnumber (req), vendorid, modeltypeid, description, imageurl, documentationurl, notes" - } - } - } - } - } - }, - "/api/models/{model_id}": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "Get one model with nested vendor + modeltype dicts", - "description": "Get one model with nested vendor + modeltype dicts\n\n**Auth:** jwt-optional\n\n**Params:** path: model_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/models/12\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "model_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "core-catalog" - ], - "summary": "Update model; honors X-Import-Mode", - "description": "Update model; honors X-Import-Mode\n\n**Auth:** admin\n\n**Params:** body: modelnumber, vendorid, modeltypeid, description, imageurl, documentationurl, notes, isactive\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/models/12 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"notes\":\"EOL 2027\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "model_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: modelnumber, vendorid, modeltypeid, description, imageurl, documentationurl, notes, isactive" - } - } - } - } - }, - "delete": { - "tags": [ - "core-catalog" - ], - "summary": "Soft-delete model", - "description": "Soft-delete model\n\n**Auth:** admin\n\n**Params:** path: model_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/models/12 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "model_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/models/{model_id}/image": { - "post": { - "tags": [ - "core-catalog" - ], - "summary": "Upload/replace model photo (saved as instance/modelimages/model-, one per model); sets imageurl", - "description": "Upload/replace model photo (saved as instance/modelimages/model-, one per model); sets imageurl\n\n**Auth:** admin\n\n**Params:** multipart/form-data: file= (.png/.jpg/.jpeg/.gif/.webp/.svg)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/models/12/image -H \"Authorization: Bearer $TOK\" -F file=@robot.jpg\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "model_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "multipart/form-data: file= (.png/.jpg/.jpeg/.gif/.webp/.svg)" - } - } - } - } - }, - "delete": { - "tags": [ - "core-catalog" - ], - "summary": "Clear imageurl and delete the uploaded file only if it lives under /api/models/image/ (external URLs untouched)", - "description": "Clear imageurl and delete the uploaded file only if it lives under /api/models/image/ (external URLs untouched)\n\n**Auth:** admin\n\n**Params:** path: model_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/models/12/image -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "model_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/models/image/{filename}": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "Serve an uploaded model image (deliberately public - asset detail pages read it without auth)", - "description": "Serve an uploaded model image (deliberately public - asset detail pages read it without auth)\n\n**Auth:** none\n\n**Params:** path: filename\n\n**Example:**\n```\ncurl http://localhost:5001/api/models/image/model-12.jpg\n```", + "summary": "Serve an uploaded blueprint image (public - kiosks read it).", + "description": "Serve an uploaded blueprint image (public - kiosks read it).\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** path: filename (path)\n\n**Example:**\n```\ncurl http://localhost:5001/api/settings/map-blueprint/example.png\n```", "security": [], "responses": { "200": { @@ -6883,21 +8693,21 @@ "in": "path", "required": true, "schema": { - "type": "string" + "type": "string", + "description": "may contain slashes" } } ] } }, - "/api/modeltypes": { - "get": { + "/api/settings/seed": { + "post": { "tags": [ - "core-catalog" + "core-settings" ], - "summary": "List model types (types the vendor MODELS catalog, not machines); exact modeltype lookup for import", - "description": "List model types (types the vendor MODELS catalog, not machines); exact modeltype lookup for import\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active, category, modeltype (exact), search (ilike)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/modeltypes?category=Equipment'\n```", + "summary": "Seed default settings if they don't exist.", + "description": "Seed default settings if they don't exist.\n\n**Auth:** permission:settings.edit\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/settings/seed -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ - {}, { "bearerAuth": [] } @@ -6918,15 +8728,339 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/settings/test-email": { + "post": { + "tags": [ + "core-settings" + ], + "summary": "Send a test email to verify SMTP configuration.", + "description": "Send a test email to verify SMTP configuration.\n\n**Auth:** permission:settings.edit\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/settings/test-email -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/setup/complete": { + "post": { + "tags": [ + "core-setup" + ], + "summary": "Flag the first-run setup as finished.", + "description": "Flag the first-run setup as finished.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/setup/complete -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/setup/create-admin": { + "post": { + "tags": [ + "core-setup" + ], + "summary": "Create the first admin. Only works while there are zero users, setup has not been marked complete, and the caller...", + "description": "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).\n\n**Auth:** none (first run only: refused once an admin exists)\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/setup/create-admin -H 'Content-Type: application/json' -d '{}'\n```", + "security": [], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/setup/needs-admin": { + "get": { + "tags": [ + "core-setup" + ], + "summary": "True when no users exist yet - the app's very first run. Public so the login screen can offer to create the first admin.", + "description": "True when no users exist yet - the app's very first run. Public so the login screen can offer to create the first admin.\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/setup/needs-admin\n```", + "security": [], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + } + } + }, + "/api/setup/seed-reference": { + "post": { + "tags": [ + "core-setup" + ], + "summary": "Seed core reference data + permissions + default settings. Idempotent - reuses the same routines as the `flask seed...", + "description": "Seed core reference data + permissions + default settings. Idempotent - reuses the same routines as the `flask seed ...` CLI commands.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/setup/seed-reference -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/setup/seed-starter": { + "post": { + "tags": [ + "core-setup" + ], + "summary": "Add common vendors that are not already present. Idempotent.", + "description": "Add common vendors that are not already present. Idempotent.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/setup/seed-starter -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/shopdbbackups": { + "get": { + "tags": [ + "core-shopdbbackups" + ], + "summary": "List every retained backup set, newest first, with a compatibility flag.", + "description": "List every retained backup set, newest first, with a compatibility flag.\n\n**Auth:** jwt + role:admin\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/shopdbbackups -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." } } }, "post": { "tags": [ - "core-catalog" + "core-shopdbbackups" ], - "summary": "Create model type (409 on duplicate); category defaults to Equipment; honors X-Import-Mode", - "description": "Create model type (409 on duplicate); category defaults to Equipment; honors X-Import-Mode\n\n**Auth:** admin\n\n**Params:** body: modeltype (req), category, description, icon\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/modeltypes -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"modeltype\":\"Controller\"}'\n```", + "summary": "Kick off an on-demand backup run.", + "description": "Kick off an on-demand backup run.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/shopdbbackups -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -6963,64 +9097,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: modeltype (req), category, description, icon" + "description": "body: JSON (fields not yet documented)" } } } } } }, - "/api/modeltypes/{type_id}": { + "/api/shopdbbackups/{snapshotid}": { "get": { "tags": [ - "core-catalog" + "core-shopdbbackups" ], - "summary": "Get one model type", - "description": "Get one model type\n\n**Auth:** jwt-optional\n\n**Params:** path: type_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/modeltypes/2\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "core-catalog" - ], - "summary": "Update model type (rename conflict-checked); honors X-Import-Mode", - "description": "Update model type (rename conflict-checked); honors X-Import-Mode\n\n**Auth:** admin\n\n**Params:** body: modeltype, category, description, icon, isactive\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/modeltypes/2 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"icon\":\"mdi-chip\"}'\n```", + "summary": "Return one set's full manifest plus a resolved compatibility verdict.", + "description": "Return one set's full manifest plus a resolved compatibility verdict.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: snapshotid (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/shopdbbackups/x -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -7055,68 +9145,7 @@ }, "parameters": [ { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: modeltype, category, description, icon, isactive" - } - } - } - } - }, - "delete": { - "tags": [ - "core-catalog" - ], - "summary": "Soft-delete model type; 409 while models use it", - "description": "Soft-delete model type; 409 while models use it\n\n**Auth:** admin\n\n**Params:** path: type_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/modeltypes/2 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", + "name": "snapshotid", "in": "path", "required": true, "schema": { @@ -7126,138 +9155,13 @@ ] } }, - "/api/businessunits": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "List business units (paginated); exact businessunit lookup for import", - "description": "List business units (paginated); exact businessunit lookup for import\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active, businessunit (exact), search (name/code ilike)\n\n**Example:**\n```\ncurl http://localhost:5001/api/businessunits\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, + "/api/shopdbbackups/{snapshotid}/restore": { "post": { "tags": [ - "core-catalog" + "core-shopdbbackups" ], - "summary": "Create business unit (409 on duplicate); honors X-Import-Mode", - "description": "Create business unit (409 on duplicate); honors X-Import-Mode\n\n**Auth:** admin\n\n**Params:** body: businessunit (req), code, description, parentid\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/businessunits -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"businessunit\":\"Blades\",\"code\":\"BLD\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: businessunit (req), code, description, parentid" - } - } - } - } - } - }, - "/api/businessunits/{bu_id}": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "Get one business unit with parent + children", - "description": "Get one business unit with parent + children\n\n**Auth:** jwt-optional\n\n**Params:** path: bu_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/businessunits/1\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "bu_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "core-catalog" - ], - "summary": "Update business unit (rename conflict-checked); honors X-Import-Mode", - "description": "Update business unit (rename conflict-checked); honors X-Import-Mode\n\n**Auth:** admin\n\n**Params:** body: businessunit, code, description, parentid, isactive\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/businessunits/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"code\":\"BL\"}'\n```", + "summary": "Queue a restore of a set, gated on schema compatibility.", + "description": "Queue a restore of a set, gated on schema compatibility.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: snapshotid (string); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/shopdbbackups/x/restore -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -7292,7 +9196,7 @@ }, "parameters": [ { - "name": "bu_id", + "name": "snapshotid", "in": "path", "required": true, "schema": { @@ -7307,100 +9211,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: businessunit, code, description, parentid, isactive" + "description": "path: snapshotid (string); body: JSON (fields not yet documented)" } } } } - }, - "delete": { - "tags": [ - "core-catalog" - ], - "summary": "Soft-delete business unit", - "description": "Soft-delete business unit\n\n**Auth:** admin\n\n**Params:** path: bu_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/businessunits/1 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "bu_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] } }, - "/api/operatingsystems": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "List operating systems (paginated); exact osname/osversion lookup for import", - "description": "List operating systems (paginated); exact osname/osversion lookup for import\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active, osname (exact), osversion (exact), search (osname ilike)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/operatingsystems?osname=Windows%2011'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, + "/api/shopdbbackups/finalize-restore": { "post": { "tags": [ - "core-catalog" + "core-shopdbbackups" ], - "summary": "Create OS (409 on duplicate osname+osversion); honors X-Import-Mode", - "description": "Create OS (409 on duplicate osname+osversion); honors X-Import-Mode\n\n**Auth:** admin\n\n**Params:** body: osname (req), osversion, architecture, endoflife\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/operatingsystems -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"osname\":\"Windows 11\",\"osversion\":\"24H2\"}'\n```", + "summary": "Run the post-restore schema upgrade in the app context.", + "description": "Run the post-restore schema upgrade in the app context.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/shopdbbackups/finalize-restore -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -7437,22 +9261,21 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: osname (req), osversion, architecture, endoflife" + "description": "body: JSON (fields not yet documented)" } } } } } }, - "/api/operatingsystems/{os_id}": { + "/api/shopdbbackups/history": { "get": { "tags": [ - "core-catalog" + "core-shopdbbackups" ], - "summary": "Get one operating system", - "description": "Get one operating system\n\n**Auth:** jwt-optional\n\n**Params:** path: os_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/operatingsystems/4\n```", + "summary": "Return the recorded history of backup and restore runs, newest first.", + "description": "Return the recorded history of backup and restore runs, newest first.\n\n**Auth:** jwt + role:admin\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/shopdbbackups/history -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -7474,27 +9297,58 @@ } } }, - "404": { - "description": "No such record." + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." } - }, - "parameters": [ + } + } + }, + "/api/shopdbbackups/settings": { + "get": { + "tags": [ + "core-shopdbbackups" + ], + "summary": "Return destination + retention config for the settings pane.", + "description": "Return destination + retention config for the settings pane.\n\n**Auth:** jwt + role:admin\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/shopdbbackups/settings -H \"Authorization: Bearer $TOK\"\n```", + "security": [ { - "name": "os_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } + "bearerAuth": [] } - ] + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } }, "put": { "tags": [ - "core-catalog" + "core-shopdbbackups" ], - "summary": "Update OS; honors X-Import-Mode", - "description": "Update OS; honors X-Import-Mode\n\n**Auth:** admin\n\n**Params:** body: osname, osversion, architecture, endoflife, isactive\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/operatingsystems/4 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"endoflife\":\"2031-10-14\"}'\n```", + "summary": "Persist the backup destination kind and its credential/config fields.", + "description": "Persist the backup destination kind and its credential/config fields.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/shopdbbackups/settings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -7522,21 +9376,8 @@ }, "403": { "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." } }, - "parameters": [ - { - "name": "os_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], "requestBody": { "required": true, "content": { @@ -7544,100 +9385,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: osname, osversion, architecture, endoflife, isactive" + "description": "body: JSON (fields not yet documented)" } } } } - }, - "delete": { - "tags": [ - "core-catalog" - ], - "summary": "Soft-delete OS", - "description": "Soft-delete OS\n\n**Auth:** admin\n\n**Params:** path: os_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/operatingsystems/4 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "os_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] } }, - "/api/customfields": { + "/api/shopdbbackups/status": { "get": { "tags": [ - "core-catalog" + "core-shopdbbackups" ], - "summary": "List custom-field definitions, ordered by sortorder", - "description": "List custom-field definitions, ordered by sortorder\n\n**Auth:** jwt-optional\n\n**Params:** assettypeid (int), active=false includes inactive\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/customfields?assettypeid=1'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "core-catalog" - ], - "summary": "Create field definition; fieldkey auto-slugged from label; reactivates a soft-deleted same-key field instead of 409", - "description": "Create field definition; fieldkey auto-slugged from label; reactivates a soft-deleted same-key field instead of 409\n\n**Auth:** admin\n\n**Params:** body: assettypeid (req), label (req), datatype (one of CUSTOM_FIELD_DATATYPES, default text), fieldkey, options (list or newline/comma text), showondetail, showonform, searchable, sortorder\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/customfields -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"assettypeid\":1,\"label\":\"Coolant Type\",\"datatype\":\"select\",\"options\":[\"Oil\",\"Water\"]}'\n```", + "summary": "Return the live status of the most recent backup and restore runs.", + "description": "Return the live status of the most recent backup and restore runs.\n\n**Auth:** jwt + role:admin\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/shopdbbackups/status -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -7666,249 +9427,17 @@ "403": { "description": "Authenticated, but not permitted." } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: assettypeid (req), label (req), datatype (one of CUSTOM_FIELD_DATATYPES, default text), fieldkey, options (list or newline/comma text), showondetail, showonform, searchable, sortorder" - } - } - } - } - } - }, - "/api/customfields/{fieldid}": { - "put": { - "tags": [ - "core-catalog" - ], - "summary": "Update field definition (label/datatype/options/flags/sortorder)", - "description": "Update field definition (label/datatype/options/flags/sortorder)\n\n**Auth:** admin\n\n**Params:** body: label, datatype, options, showondetail, showonform, isactive, searchable, sortorder\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/customfields/7 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"searchable\":true}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "fieldid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: label, datatype, options, showondetail, showonform, isactive, searchable, sortorder" - } - } - } - } - }, - "delete": { - "tags": [ - "core-catalog" - ], - "summary": "Hard-delete field definition AND all stored values for it", - "description": "Hard-delete field definition AND all stored values for it\n\n**Auth:** admin\n\n**Params:** path: fieldid\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/customfields/7 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "fieldid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/customfields/asset/{assetid}": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "Active field defs for the asset's type merged with the asset's stored values", - "description": "Active field defs for the asset's type merged with the asset's stored values\n\n**Auth:** jwt-optional\n\n**Params:** path: assetid\n\n**Example:**\n```\ncurl http://localhost:5001/api/customfields/asset/42\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "assetid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "core-catalog" - ], - "summary": "Upsert per-asset values; empty string clears a value; only fields of the asset's type accepted", - "description": "Upsert per-asset values; empty string clears a value; only fields of the asset's type accepted\n\n**Auth:** admin\n\n**Params:** body: {values: {fieldid: value, ...}}\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/customfields/asset/42 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"values\":{\"7\":\"Oil\"}}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "assetid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: {values: {fieldid: value, ...}}" - } - } - } } } }, "/api/supportteams": { "get": { "tags": [ - "core-catalog" + "core-supportteams" ], - "summary": "List support teams with contacts; exact teamname lookup for import (not paginated)", - "description": "List support teams with contacts; exact teamname lookup for import (not paginated)\n\n**Auth:** jwt-optional\n\n**Params:** active, teamname (exact)\n\n**Example:**\n```\ncurl http://localhost:5001/api/supportteams\n```", + "summary": "List support teams (with contacts). ?active and ?teamname filters.", + "description": "List support teams (with contacts). ?active and ?teamname filters.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/supportteams -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -7929,15 +9458,21 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." } } }, "post": { "tags": [ - "core-catalog" + "core-supportteams" ], - "summary": "Create support team (409 on duplicate); audit-logged; honors X-Import-Mode", - "description": "Create support team (409 on duplicate); audit-logged; honors X-Import-Mode\n\n**Auth:** admin\n\n**Params:** body: teamname (req), teamurl, webhookurl, isactive\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/supportteams -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"teamname\":\"CNC Support\"}'\n```", + "summary": "Create a new support team.", + "description": "Create a new support team.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/supportteams -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -7974,7 +9509,7 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: teamname (req), teamurl, webhookurl, isactive" + "description": "body: JSON (fields not yet documented)" } } } @@ -7982,14 +9517,13 @@ } }, "/api/supportteams/{team_id}": { - "get": { + "delete": { "tags": [ - "core-catalog" + "core-supportteams" ], - "summary": "Get one support team with contacts", - "description": "Get one support team with contacts\n\n**Auth:** jwt-optional\n\n**Params:** path: team_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/supportteams/2\n```", + "summary": "Delete a support team; 409 while any application references it.", + "description": "Delete a support team; 409 while any application references it.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: team_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/supportteams/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -8011,6 +9545,12 @@ } } }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, "404": { "description": "No such record." } @@ -8021,17 +9561,66 @@ "in": "path", "required": true, "schema": { - "type": "string" + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "core-supportteams" + ], + "summary": "Get a single support team with its contacts.", + "description": "Get a single support team with its contacts.\n\n**Auth:** permission:assets.view\n\n**Params:** path: team_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/supportteams/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "team_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" } } ] }, "put": { "tags": [ - "core-catalog" + "core-supportteams" ], - "summary": "Update support team (rename conflict-checked); audit-logged; honors X-Import-Mode", - "description": "Update support team (rename conflict-checked); audit-logged; honors X-Import-Mode\n\n**Auth:** admin\n\n**Params:** body: teamname, teamurl, webhookurl, isactive\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/supportteams/2 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"webhookurl\":\"https://hooks/x\"}'\n```", + "summary": "Update a support team.", + "description": "Update a support team.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: team_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/supportteams/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -8070,7 +9659,7 @@ "in": "path", "required": true, "schema": { - "type": "string" + "type": "integer" } } ], @@ -8081,69 +9670,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: teamname, teamurl, webhookurl, isactive" + "description": "path: team_id (int); body: JSON (fields not yet documented)" } } } } - }, - "delete": { - "tags": [ - "core-catalog" - ], - "summary": "Hard-delete team (cascade removes contacts); 409 while applications reference it; audit-logged", - "description": "Hard-delete team (cascade removes contacts); 409 while applications reference it; audit-logged\n\n**Auth:** admin\n\n**Params:** path: team_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/supportteams/2 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "team_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] } }, "/api/supportteams/{team_id}/contacts": { "post": { "tags": [ - "core-catalog" + "core-supportteams" ], - "summary": "Add contact to a team; audit-logged; honors X-Import-Mode", - "description": "Add contact to a team; audit-logged; honors X-Import-Mode\n\n**Auth:** admin\n\n**Params:** body: name (req), sso, sortorder, isactive\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/supportteams/2/contacts -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"name\":\"Jane Doe\",\"sso\":\"212345678\"}'\n```", + "summary": "Add a contact to a support team.", + "description": "Add a contact to a support team.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: team_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/supportteams/1/contacts -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -8182,7 +9722,7 @@ "in": "path", "required": true, "schema": { - "type": "string" + "type": "integer" } } ], @@ -8193,7 +9733,7 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: name (req), sso, sortorder, isactive" + "description": "path: team_id (int); body: JSON (fields not yet documented)" } } } @@ -8201,12 +9741,12 @@ } }, "/api/supportteams/{team_id}/contacts/{contact_id}": { - "put": { + "delete": { "tags": [ - "core-catalog" + "core-supportteams" ], - "summary": "Update a team contact; audit-logged; honors X-Import-Mode", - "description": "Update a team contact; audit-logged; honors X-Import-Mode\n\n**Auth:** admin\n\n**Params:** body: name, sso, sortorder, isactive\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/supportteams/2/contacts/9 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"sortorder\":1}'\n```", + "summary": "Delete a support-team contact.", + "description": "Delete a support-team contact.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: team_id (int), contact_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/supportteams/1/contacts/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -8245,7 +9785,7 @@ "in": "path", "required": true, "schema": { - "type": "string" + "type": "integer" } }, { @@ -8253,29 +9793,17 @@ "in": "path", "required": true, "schema": { - "type": "string" + "type": "integer" } } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: name, sso, sortorder, isactive" - } - } - } - } + ] }, - "delete": { + "put": { "tags": [ - "core-catalog" + "core-supportteams" ], - "summary": "Hard-delete a team contact; audit-logged", - "description": "Hard-delete a team contact; audit-logged\n\n**Auth:** admin\n\n**Params:** path: team_id, contact_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/supportteams/2/contacts/9 -H \"Authorization: Bearer $TOK\"\n```", + "summary": "Update a support-team contact.", + "description": "Update a support-team contact.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: team_id (int), contact_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/supportteams/1/contacts/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -8314,7 +9842,7 @@ "in": "path", "required": true, "schema": { - "type": "string" + "type": "integer" } }, { @@ -8322,21 +9850,32 @@ "in": "path", "required": true, "schema": { - "type": "string" + "type": "integer" } } - ] + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: team_id (int), contact_id (int); body: JSON (fields not yet documented)" + } + } + } + } } }, - "/api/applications": { + "/api/users": { "get": { "tags": [ - "core-catalog" + "core-users" ], - "summary": "List applications (paginated) with installedcount; hidden apps excluded unless showhidden=true; exact appname lookup...", - "description": "List applications (paginated) with installedcount; hidden apps excluded unless showhidden=true; exact appname lookup for import\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active, showhidden, installable (true/false), appname (exact), search (name/description ilike)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/applications?installable=true'\n```", + "summary": "List all users.", + "description": "List all users.\n\n**Auth:** jwt + role:admin\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/users -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -8357,15 +9896,21 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." } } }, "post": { "tags": [ - "core-catalog" + "core-users" ], - "summary": "Create application (409 on duplicate name); audit-logged; honors X-Import-Mode", - "description": "Create application (409 on duplicate name); audit-logged; honors X-Import-Mode\n\n**Auth:** permission:applications.create\n\n**Params:** body: appname (req), appdescription, supportteamid, isinstallable, applicationnotes, installpath, applicationlink, documentationpath, ishidden, isprinter, islicenced, isrequired, image\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/applications -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"appname\":\"PC-DMIS\",\"isinstallable\":true}'\n```", + "summary": "Create a new user.", + "description": "Create a new user.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/users -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -8402,125 +9947,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: appname (req), appdescription, supportteamid, isinstallable, applicationnotes, installpath, applicationlink, documentationpath, ishidden, isprinter, islicenced, isrequired, image" + "description": "body: JSON (fields not yet documented)" } } } } } }, - "/api/applications/{app_id}": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "Get one application with active versions, installedcount, and linked KB articles", - "description": "Get one application with active versions, installedcount, and linked KB articles\n\n**Auth:** jwt-optional\n\n**Params:** path: app_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/applications/15\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "app_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "core-catalog" - ], - "summary": "Update application (rename conflict-checked); field-level change diff audit-logged; honors X-Import-Mode", - "description": "Update application (rename conflict-checked); field-level change diff audit-logged; honors X-Import-Mode\n\n**Auth:** permission:applications.edit\n\n**Params:** body: any of appname, appdescription, supportteamid, isinstallable, applicationnotes, installpath, applicationlink, documentationpath, ishidden, isprinter, islicenced, isrequired, image, isactive\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/applications/15 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"supportteamid\":2}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "app_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: any of appname, appdescription, supportteamid, isinstallable, applicationnotes, installpath, applicationlink, documentationpath, ishidden, isprinter, islicenced, isrequired, image, isactive" - } - } - } - } - }, + "/api/users/{userid}": { "delete": { "tags": [ - "core-catalog" + "core-users" ], - "summary": "Soft-delete application; audit-logged", - "description": "Soft-delete application; audit-logged\n\n**Auth:** permission:applications.delete\n\n**Params:** path: app_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/applications/15 -H \"Authorization: Bearer $TOK\"\n```", + "summary": "Delete a user.", + "description": "Delete a user.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: userid (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/users/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -8555,220 +9995,21 @@ }, "parameters": [ { - "name": "app_id", + "name": "userid", "in": "path", "required": true, "schema": { - "type": "string" - } - } - ] - } - }, - "/api/applications/{app_id}/versions": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "List active versions of an application (desc)", - "description": "List active versions of an application (desc)\n\n**Auth:** jwt-optional\n\n**Params:** path: app_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/applications/15/versions\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "app_id", - "in": "path", - "required": true, - "schema": { - "type": "string" + "type": "integer" } } ] }, - "post": { - "tags": [ - "core-catalog" - ], - "summary": "Create app version (409 on duplicate version per app); honors X-Import-Mode for legacy dates", - "description": "Create app version (409 on duplicate version per app); honors X-Import-Mode for legacy dates\n\n**Auth:** permission:applications.create\n\n**Params:** body: version (req), releasedate, notes\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/applications/15/versions -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"version\":\"2024.2\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "app_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: version (req), releasedate, notes" - } - } - } - } - } - }, - "/api/applications/{app_id}/installed": { "get": { "tags": [ - "core-catalog" + "core-users" ], - "summary": "List computers with this app installed (503 if computers plugin absent)", - "description": "List computers with this app installed (503 if computers plugin absent)\n\n**Auth:** jwt-optional\n\n**Params:** path: app_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/applications/15/installed\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "app_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/applications/machines/{machine_id}": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "List apps installed on a computer (machine_id is a computerid; 503 without computers plugin)", - "description": "List apps installed on a computer (machine_id is a computerid; 503 without computers plugin)\n\n**Auth:** jwt-optional\n\n**Params:** path: machine_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/applications/machines/8\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "machine_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "post": { - "tags": [ - "core-catalog" - ], - "summary": "Install an app on a computer; reactivates a prior soft-deleted install; 409 if already installed", - "description": "Install an app on a computer; reactivates a prior soft-deleted install; 409 if already installed\n\n**Auth:** permission:applications.create\n\n**Params:** body: appid (req), appversionid\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/applications/machines/8 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"appid\":15}'\n```", + "summary": "Get a single user.", + "description": "Get a single user.\n\n**Auth:** jwt\n\n**Params:** path: userid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/users/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -8803,590 +10044,7 @@ }, "parameters": [ { - "name": "machine_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: appid (req), appversionid" - } - } - } - } - } - }, - "/api/applications/machines/{machine_id}/{app_id}": { - "delete": { - "tags": [ - "core-catalog" - ], - "summary": "Uninstall (soft-delete install row) an app from a computer", - "description": "Uninstall (soft-delete install row) an app from a computer\n\n**Auth:** permission:applications.delete\n\n**Params:** path: machine_id, app_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/applications/machines/8/15 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "machine_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "app_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "core-catalog" - ], - "summary": "Update an installed-app row (change appversionid)", - "description": "Update an installed-app row (change appversionid)\n\n**Auth:** permission:applications.edit\n\n**Params:** body: appversionid\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/applications/machines/8/15 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"appversionid\":3}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "machine_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "app_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: appversionid" - } - } - } - } - } - }, - "/api/applications/{app_id}/image": { - "post": { - "tags": [ - "core-catalog" - ], - "summary": "Upload or replace an application's image; sets application.image to the served URL", - "description": "Upload or replace an application's image; sets application.image to the served URL\n\n**Auth:** jwt + applications.edit\n\n**Params:** multipart/form-data: file= (png/jpg/gif/webp/svg/ico)\n\n**Example:**\n```\ncurl -X POST -H 'Authorization: Bearer ' -F file=@logo.png http://localhost:5001/api/applications/5/image\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "app_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "multipart/form-data: file= (png/jpg/gif/webp/svg/ico)" - } - } - } - } - }, - "delete": { - "tags": [ - "core-catalog" - ], - "summary": "Remove an application's image and clear the field", - "description": "Remove an application's image and clear the field\n\n**Auth:** jwt + applications.edit\n\n**Params:** none\n\n**Example:**\n```\ncurl -X DELETE -H 'Authorization: Bearer ' http://localhost:5001/api/applications/5/image\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "app_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/applications/image/{filename}": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "Serve an uploaded application image. Public: application tiles render before login", - "description": "Serve an uploaded application image. Public: application tiles render before login\n\n**Auth:** none\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/applications/image/application-5.png\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "filename", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/applications/{app_id}/package": { - "post": { - "tags": [ - "core-catalog" - ], - "summary": "Upload or replace the installer; sets installpath to the download URL", - "description": "Upload or replace the installer; sets installpath to the download URL\n\n**Auth:** jwt + applications.edit\n\n**Params:** multipart/form-data: file= (exe/msi/msp/zip/7z/cab/iso/appx/msix/ps1/bat/txt/pdf), max 500MB\n\n**Example:**\n```\ncurl -X POST -H 'Authorization: Bearer ' -F file=@setup.msi http://localhost:5001/api/applications/5/package\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "app_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "multipart/form-data: file= (exe/msi/msp/zip/7z/cab/iso/appx/msix/ps1/bat/txt/pdf), max 500MB" - } - } - } - } - }, - "delete": { - "tags": [ - "core-catalog" - ], - "summary": "Remove an uploaded installer; clears installpath only when it pointed at the upload", - "description": "Remove an uploaded installer; clears installpath only when it pointed at the upload\n\n**Auth:** jwt + applications.edit\n\n**Params:** none\n\n**Example:**\n```\ncurl -X DELETE -H 'Authorization: Bearer ' http://localhost:5001/api/applications/5/package\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "app_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/applications/package/{filename}": { - "get": { - "tags": [ - "core-catalog" - ], - "summary": "Download an uploaded installer as an attachment. Authenticated: licensed vendor software", - "description": "Download an uploaded installer as an attachment. Authenticated: licensed vendor software\n\n**Auth:** jwt + applications.view\n\n**Params:** none\n\n**Example:**\n```\ncurl -OJ -H 'Authorization: Bearer ' http://localhost:5001/api/applications/package/application-5.msi\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "filename", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/computers/types": { - "get": { - "tags": [ - "plugin-computers" - ], - "summary": "List computer types, paginated, active-only by default", - "description": "List computer types, paginated, active-only by default\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active (default true; 'false' includes inactive), search (ilike on computertype)\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/computers/types?search=kiosk&per_page=50'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "plugin-computers" - ], - "summary": "Create (or reactivate) a computer type", - "description": "Create (or reactivate) a computer type\n\n**Auth:** permission:computers.create (jwt_required)\n\n**Params:** body: computertype (required), description, icon, color; matching a deactivated type revives it instead of 409\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"computertype\":\"Shopfloor\",\"color\":\"#0066cc\"}' 'http://localhost:5001/api/computers/types'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: computertype (required), description, icon, color; matching a deactivated type revives it instead of 409" - } - } - } - } - } - }, - "/api/computers/types/{type_id}": { - "get": { - "tags": [ - "plugin-computers" - ], - "summary": "Get a single computer type by ID", - "description": "Get a single computer type by ID\n\n**Auth:** jwt-optional\n\n**Params:** type_id in path\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/computers/types/3'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", + "name": "userid", "in": "path", "required": true, "schema": { @@ -9397,10 +10055,10 @@ }, "put": { "tags": [ - "plugin-computers" + "core-users" ], - "summary": "Update a computer type", - "description": "Update a computer type\n\n**Auth:** permission:computers.edit (jwt_required)\n\n**Params:** body: computertype, description, icon, color, isactive; 409 on duplicate name\n\n**Example:**\n```\ncurl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"isactive\":false}' 'http://localhost:5001/api/computers/types/3'\n```", + "summary": "Update a user.", + "description": "Update a user.\n\n**Auth:** jwt\n\n**Params:** path: userid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/users/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -9435,7 +10093,7 @@ }, "parameters": [ { - "name": "type_id", + "name": "userid", "in": "path", "required": true, "schema": { @@ -9450,18 +10108,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: computertype, description, icon, color, isactive; 409 on duplicate name" + "description": "path: userid (int); body: JSON (fields not yet documented)" } } } } - }, - "delete": { + } + }, + "/api/users/permissions": { + "get": { "tags": [ - "plugin-computers" + "core-users" ], - "summary": "Hard-delete a computer type when unused", - "description": "Hard-delete a computer type when unused\n\n**Auth:** permission:computers.delete (jwt_required)\n\n**Params:** type_id in path; 409 if any Computer still uses the type\n\n**Example:**\n```\ncurl -X DELETE -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/computers/types/3'\n```", + "summary": "List assignable permissions grouped by category.", + "description": "List assignable permissions grouped by category.\n\n**Auth:** jwt\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/users/permissions -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -9489,32 +10149,18 @@ }, "403": { "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] + } } }, - "/api/computers/protocols": { + "/api/users/roles": { "get": { "tags": [ - "plugin-computers" + "core-users" ], - "summary": "List remote-access protocols (VNC/WinRM/RDP catalog), unpaginated", - "description": "List remote-access protocols (VNC/WinRM/RDP catalog), unpaginated\n\n**Auth:** jwt-optional\n\n**Params:** active (default true; 'false' includes disabled)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/computers/protocols?active=false'\n```", + "summary": "List all roles with their permissions.", + "description": "List all roles with their permissions.\n\n**Auth:** jwt\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/users/roles -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -9535,15 +10181,21 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." } } }, "post": { "tags": [ - "plugin-computers" + "core-users" ], - "summary": "Create an access protocol", - "description": "Create an access protocol\n\n**Auth:** permission:computers.edit (jwt_required)\n\n**Params:** body: name, scheme, linktemplate (all required), defaultport, isactive; 409 on duplicate name\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"name\":\"VNC\",\"scheme\":\"vnc\",\"defaultport\":5900,\"linktemplate\":\"vnc://{host}:{port}\"}' 'http://localhost:5001/api/computers/protocols'\n```", + "summary": "Create a new role.", + "description": "Create a new role.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/users/roles -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -9580,20 +10232,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: name, scheme, linktemplate (all required), defaultport, isactive; 409 on duplicate name" + "description": "body: JSON (fields not yet documented)" } } } } } }, - "/api/computers/protocols/{protocol_id}": { - "put": { + "/api/users/roles/{roleid}": { + "delete": { "tags": [ - "plugin-computers" + "core-users" ], - "summary": "Update one remote-access protocol in the catalog (VNC, WinRM, RDP) that PCs report against", - "description": "Update one remote-access protocol in the catalog (VNC, WinRM, RDP) that PCs report against\n\n**Auth:** permission:computers.edit (jwt_required)\n\n**Params:** protocol_id in path; body: any of protocolname, port, isactive\n\n**Example:**\n```\ncurl -X PATCH -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"port\":5900}' http://localhost:5001/api/computers/protocols/3\n```", + "summary": "Delete a role.", + "description": "Delete a role.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: roleid (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/users/roles/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -9628,11 +10280,60 @@ }, "parameters": [ { - "name": "protocol_id", + "name": "roleid", "in": "path", "required": true, "schema": { - "type": "string" + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-users" + ], + "summary": "Update a role.", + "description": "Update a role.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: roleid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/users/roles/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "roleid", + "in": "path", + "required": true, + "schema": { + "type": "integer" } } ], @@ -9643,18 +10344,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "protocol_id in path; body: any of protocolname, port, isactive" + "description": "path: roleid (int); body: JSON (fields not yet documented)" } } } } - }, - "patch": { + } + }, + "/api/vendors": { + "get": { "tags": [ - "plugin-computers" + "core-vendors" ], - "summary": "Update one remote-access protocol in the catalog (VNC, WinRM, RDP) that PCs report against", - "description": "Update one remote-access protocol in the catalog (VNC, WinRM, RDP) that PCs report against\n\n**Auth:** permission:computers.edit (jwt_required)\n\n**Params:** protocol_id in path; body: any of protocolname, port, isactive\n\n**Example:**\n```\ncurl -X PATCH -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"port\":5900}' http://localhost:5001/api/computers/protocols/3\n```", + "summary": "List all vendors.", + "description": "List all vendors.\n\n**Auth:** permission:assets.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/vendors -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -9682,40 +10385,65 @@ }, "403": { "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." } - }, - "parameters": [ + } + }, + "post": { + "tags": [ + "core-vendors" + ], + "summary": "Create a new vendor.", + "description": "Create a new vendor.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/vendors -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ { - "name": "protocol_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } + "bearerAuth": [] } ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, "requestBody": { - "required": false, + "required": true, "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true, - "description": "protocol_id in path; body: any of protocolname, port, isactive" + "description": "body: JSON (fields not yet documented)" } } } } - }, + } + }, + "/api/vendors/{vendor_id}": { "delete": { "tags": [ - "plugin-computers" + "core-vendors" ], - "summary": "Delete an access protocol (soft-deactivate when in use)", - "description": "Delete an access protocol (soft-deactivate when in use)\n\n**Auth:** permission:computers.edit (jwt_required)\n\n**Params:** protocol_id in path; if referenced by any ComputerAccess it deactivates instead of deleting\n\n**Example:**\n```\ncurl -X DELETE -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/computers/protocols/2'\n```", + "summary": "Delete (deactivate) a vendor.", + "description": "Delete (deactivate) a vendor.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: vendor_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/vendors/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -9750,7 +10478,168 @@ }, "parameters": [ { - "name": "protocol_id", + "name": "vendor_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "core-vendors" + ], + "summary": "Get a single vendor.", + "description": "Get a single vendor.\n\n**Auth:** permission:assets.view\n\n**Params:** path: vendor_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/vendors/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "vendor_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "core-vendors" + ], + "summary": "Update a vendor.", + "description": "Update a vendor.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: vendor_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/vendors/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "vendor_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: vendor_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/backups/asset/{assetid}": { + "get": { + "tags": [ + "plugin-backups" + ], + "summary": "Revision history for one asset, newest first.", + "description": "Revision history for one asset, newest first.\n\n**Auth:** permission:backups.view\n\n**Params:** path: assetid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/backups/asset/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "assetid", "in": "path", "required": true, "schema": { @@ -9760,15 +10649,14 @@ ] } }, - "/api/computers/display-kiosks": { + "/api/backups/asset/{assetid}/info": { "get": { "tags": [ - "plugin-computers" + "plugin-backups" ], - "summary": "List display-kiosk computers with derived F. FQDN for the Dashboard Defaults picker", - "description": "List display-kiosk computers with derived F. FQDN for the Dashboard Defaults picker\n\n**Auth:** jwt-optional\n\n**Params:** none; uses pctype mapping for gea-shopfloor-display (default 'Kiosk') and display_fqdn_domain setting\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/computers/display-kiosks'\n```", + "summary": "A kind's 'at a glance' card, built from its LATEST revision.", + "description": "A kind's 'at a glance' card, built from its LATEST revision.\n\n**Auth:** permission:backups.view\n\n**Params:** path: assetid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/backups/asset/1/info -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -9789,19 +10677,266 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "assetid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/backups/dashboard/stale": { + "get": { + "tags": [ + "plugin-backups" + ], + "summary": "Chains whose backup has stopped running.", + "description": "Chains whose backup has stopped running.\n\n**Auth:** permission:backups.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/backups/dashboard/stale -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." } } } }, + "/api/backups/kinds": { + "get": { + "tags": [ + "plugin-backups" + ], + "summary": "Registered backup kinds, for UI that needs to enumerate them.", + "description": "Registered backup kinds, for UI that needs to enumerate them.\n\n**Auth:** permission:backups.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/backups/kinds -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/backups/revisions/{backuprevisionid}": { + "get": { + "tags": [ + "plugin-backups" + ], + "summary": "One revision, including its parsed payload where there is one.", + "description": "One revision, including its parsed payload where there is one.\n\n**Auth:** permission:backups.view\n\n**Params:** path: backuprevisionid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/backups/revisions/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "backuprevisionid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/backups/revisions/{backuprevisionid}/diff": { + "get": { + "tags": [ + "plugin-backups" + ], + "summary": "Diff two revisions of the same asset+kind.", + "description": "Diff two revisions of the same asset+kind.\n\n**Auth:** permission:backups.view\n\n**Params:** path: backuprevisionid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/backups/revisions/1/diff -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "backuprevisionid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/backups/revisions/{backuprevisionid}/download": { + "get": { + "tags": [ + "plugin-backups" + ], + "summary": "Render a revision back to its native file format.", + "description": "Render a revision back to its native file format.\n\n**Auth:** permission:backups.download\n\n**Params:** path: backuprevisionid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/backups/revisions/1/download -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "backuprevisionid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, "/api/computers": { "get": { "tags": [ "plugin-computers" ], - "summary": "List computers (asset + computer + resolved access links), filtered and paginated", - "description": "List computers (asset + computer + resolved access links), filtered and paginated\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active, search (assetnumber/name/serialnumber/hostname/gaugelabreference/maintenancereference ilike), assetnumber (exact, for idempotent import), typeid|type_id, osid|os_id, locationid|location_id, businessunitid|businessunit_id, shopfloor (true/false), sort (hostname|assetnumber|name|lastreporteddate), dir (asc|desc)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/computers?shopfloor=true&sort=lastreporteddate&dir=desc&per_page=25'\n```", + "summary": "List all computers with filtering and pagination.", + "description": "List all computers with filtering and pagination.\n\n**Auth:** permission:computers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -9822,6 +10957,12 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." } } }, @@ -9829,8 +10970,8 @@ "tags": [ "plugin-computers" ], - "summary": "Create a computer (Asset + Computer records, optional primary IP and access methods), audit-logged", - "description": "Create a computer (Asset + Computer records, optional primary IP and access methods), audit-logged\n\n**Auth:** permission:computers.create (jwt_required)\n\n**Params:** body: assetnumber (required); name, serialnumber, gaugelabreference, maintenancereference, statusid, locationid, businessunitid, mapx, mapy, notes, computertypeid, hostname, osid, vendorid, modelnumberid, loggedinuser, lastreporteddate, lastboottime, ipaddress (creates primary IP communication), accessmethods [{protocolid, portoverride?}]; X-Import-Mode header preserves legacy timestamps; 409 on duplicate assetnumber or hostname\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"assetnumber\":\"PC-0042\",\"hostname\":\"tsgwp00042\",\"computertypeid\":1,\"osid\":2,\"ipaddress\":\"10.1.2.3\",\"accessmethods\":[{\"protocolid\":1}]}' 'http://localhost:5001/api/computers'\n```", + "summary": "Create new computer (creates both Asset and Computer records).", + "description": "Create new computer (creates both Asset and Computer records).\n\n**Auth:** permission:computers.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/computers -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -9867,7 +11008,7 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: assetnumber (required); name, serialnumber, gaugelabreference, maintenancereference, statusid, locationid, businessunitid, mapx, mapy, notes, computertypeid, hostname, osid, vendorid, modelnumberid, loggedinuser, lastreporteddate, lastboottime, ipaddress (creates primary IP communication), accessmethods [{protocolid, portoverride?}]; X-Import-Mode header preserves legacy timestamps; 409 on duplicate assetnumber or hostname" + "description": "body: JSON (fields not yet documented)" } } } @@ -9875,14 +11016,13 @@ } }, "/api/computers/{computer_id}": { - "get": { + "delete": { "tags": [ "plugin-computers" ], - "summary": "Get one computer with full detail: asset fields, computer extension, communications, resolved access links", - "description": "Get one computer with full detail: asset fields, computer extension, communications, resolved access links\n\n**Auth:** jwt-optional\n\n**Params:** computer_id in path\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/computers/42'\n```", + "summary": "Delete (soft delete) computer.", + "description": "Delete (soft delete) computer.\n\n**Auth:** permission:computers.delete\n\n**Params:** path: computer_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/computers/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -9904,6 +11044,61 @@ } } }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "computer_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-computers" + ], + "summary": "Get a single computer with full details.", + "description": "Get a single computer with full details.\n\n**Auth:** permission:computers.view\n\n**Params:** path: computer_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, "404": { "description": "No such record." } @@ -9923,8 +11118,8 @@ "tags": [ "plugin-computers" ], - "summary": "Update a computer's asset and extension fields", - "description": "Update a computer's asset and extension fields\n\n**Auth:** permission:computers.edit (jwt_required)\n\n**Params:** body: any asset field (assetnumber, name, serialnumber, gaugelabreference, maintenancereference, statusid, locationid, businessunitid, mapx, mapy, notes, isactive) or computer field (computertypeid, hostname, osid, vendorid, modelnumberid, loggedinuser, lastreporteddate, lastboottime); ipaddress upserts/clears the primary IP communication; accessmethods replaces protocol list; 409 on assetnumber/hostname conflict; changes audit-logged\n\n**Example:**\n```\ncurl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"locationid\":7,\"ipaddress\":\"10.1.2.4\"}' 'http://localhost:5001/api/computers/42'\n```", + "summary": "Update computer (both Asset and Computer records).", + "description": "Update computer (both Asset and Computer records).\n\n**Auth:** permission:computers.edit\n\n**Params:** path: computer_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/computers/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -9974,18 +11169,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: any asset field (assetnumber, name, serialnumber, gaugelabreference, maintenancereference, statusid, locationid, businessunitid, mapx, mapy, notes, isactive) or computer field (computertypeid, hostname, osid, vendorid, modelnumberid, loggedinuser, lastreporteddate, lastboottime); ipaddress upserts/clears the primary IP communication; accessmethods replaces protocol list; 409 on assetnumber/hostname conflict; changes audit-logged" + "description": "path: computer_id (int); body: JSON (fields not yet documented)" } } } } - }, - "delete": { + } + }, + "/api/computers/{computer_id}/apps": { + "get": { "tags": [ "plugin-computers" ], - "summary": "Soft-delete a computer (sets asset isactive=false), audit-logged", - "description": "Soft-delete a computer (sets asset isactive=false), audit-logged\n\n**Auth:** permission:computers.delete (jwt_required)\n\n**Params:** computer_id in path\n\n**Example:**\n```\ncurl -X DELETE -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/computers/42'\n```", + "summary": "Get all installed applications for a computer.", + "description": "Get all installed applications for a computer.\n\n**Auth:** permission:computers.view\n\n**Params:** path: computer_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/1/apps -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -10028,151 +11225,13 @@ } } ] - } - }, - "/api/computers/by-asset/{asset_id}": { - "get": { - "tags": [ - "plugin-computers" - ], - "summary": "Get computer record by its core asset ID", - "description": "Get computer record by its core asset ID\n\n**Auth:** jwt-optional\n\n**Params:** asset_id in path\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/computers/by-asset/1234'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - } - }, - "/api/computers/by-hostname/{hostname}": { - "get": { - "tags": [ - "plugin-computers" - ], - "summary": "Get computer record by hostname", - "description": "Get computer record by hostname\n\n**Auth:** jwt-optional\n\n**Params:** hostname in path (exact match)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/computers/by-hostname/WKSTN0042'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "hostname", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/computers/{computer_id}/apps": { - "get": { - "tags": [ - "plugin-computers" - ], - "summary": "List installed applications on a computer", - "description": "List installed applications on a computer\n\n**Auth:** jwt-optional\n\n**Params:** computer_id in path; returns active installs only\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/computers/42/apps'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "computer_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] }, "post": { "tags": [ "plugin-computers" ], - "summary": "Record an application install on a computer", - "description": "Record an application install on a computer\n\n**Auth:** permission:computers.create (jwt_required)\n\n**Params:** body: appid (required, must exist in Applications), appversionid; reactivates a soft-deleted install; 409 if already installed\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"appid\":17,\"appversionid\":3}' 'http://localhost:5001/api/computers/42/apps'\n```", + "summary": "Add an installed application to a computer.", + "description": "Add an installed application to a computer.\n\n**Auth:** permission:computers.create\n\n**Params:** path: computer_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/computers/1/apps -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -10222,7 +11281,7 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: appid (required, must exist in Applications), appversionid; reactivates a soft-deleted install; 409 if already installed" + "description": "path: computer_id (int); body: JSON (fields not yet documented)" } } } @@ -10234,8 +11293,8 @@ "tags": [ "plugin-computers" ], - "summary": "Soft-remove an installed application (isactive=false)", - "description": "Soft-remove an installed application (isactive=false)\n\n**Auth:** permission:computers.delete (jwt_required)\n\n**Params:** computer_id and app_id in path\n\n**Example:**\n```\ncurl -X DELETE -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/computers/42/apps/17'\n```", + "summary": "Remove an installed application from a computer.", + "description": "Remove an installed application from a computer.\n\n**Auth:** permission:computers.delete\n\n**Params:** path: computer_id (int), app_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/computers/1/apps/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -10293,8 +11352,8 @@ "tags": [ "plugin-computers" ], - "summary": "Agent status check-in: refresh last-reported timestamp plus logged-in user and boot time", - "description": "Agent status check-in: refresh last-reported timestamp plus logged-in user and boot time\n\n**Auth:** permission:computers.create (jwt_required)\n\n**Params:** body (all optional): loggedinuser, lastboottime; server sets lastreporteddate to now (UTC)\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"loggedinuser\":\"jsmith\",\"lastboottime\":\"2026-07-30T06:00:00\"}' 'http://localhost:5001/api/computers/42/report'\n```", + "summary": "Report computer status (for agent-based reporting).", + "description": "Report computer status (for agent-based reporting).\n\n**Auth:** permission:computers.create\n\n**Params:** path: computer_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/computers/1/report -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -10344,22 +11403,21 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body (all optional): loggedinuser, lastboottime; server sets lastreporteddate to now (UTC)" + "description": "path: computer_id (int); body: JSON (fields not yet documented)" } } } } } }, - "/api/computers/dashboard/summary": { + "/api/computers/asset/{asset_id}/policypanel": { "get": { "tags": [ "plugin-computers" ], - "summary": "Dashboard counts: total active computers, breakdown by type and OS, shopfloor vs non-shopfloor", - "description": "Dashboard counts: total active computers, breakdown by type and OS, shopfloor vs non-shopfloor\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/computers/dashboard/summary'\n```", + "summary": "Keyvalue panel: which policy governs this endpoint, and what it allows.", + "description": "Keyvalue panel: which policy governs this endpoint, and what it allows.\n\n**Auth:** permission:computers.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/asset/1/policypanel -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -10380,6 +11438,165 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/computers/by-asset/{asset_id}": { + "get": { + "tags": [ + "plugin-computers" + ], + "summary": "Get computer data by asset ID.", + "description": "Get computer data by asset ID.\n\n**Auth:** permission:computers.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/by-asset/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/computers/by-hostname/{hostname}": { + "get": { + "tags": [ + "plugin-computers" + ], + "summary": "Get computer by hostname.", + "description": "Get computer by hostname.\n\n**Auth:** permission:computers.view\n\n**Params:** path: hostname (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/by-hostname/x -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "hostname", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ] + } + }, + "/api/computers/client-script": { + "get": { + "tags": [ + "plugin-computers" + ], + "summary": "The collector reporter, stamped with THIS site's values.", + "description": "The collector reporter, stamped with THIS site's values.\n\n**Auth:** jwt + role:admin\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/client-script -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." } } } @@ -10389,8 +11606,8 @@ "tags": [ "plugin-computers" ], - "summary": "Dashboard card: PCs that WERE reporting, have stopped, and are still meant to be in use - a PC that never reported is...", - "description": "Dashboard card: PCs that WERE reporting, have stopped, and are still meant to be in use - a PC that never reported is not evidence of a fault\n\n**Auth:** permission:computers.view\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/dashboard/quiet\n```", + "summary": "PCs that WERE reporting, have stopped, and are still meant to be in use.", + "description": "PCs that WERE reporting, have stopped, and are still meant to be in use.\n\n**Auth:** permission:computers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/dashboard/quiet -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -10427,8 +11644,8 @@ "tags": [ "plugin-computers" ], - "summary": "Dashboard card: machine numbers claimed by more than one PC, which is either a swap in progress or a mis-typed number", - "description": "Dashboard card: machine numbers claimed by more than one PC, which is either a swap in progress or a mis-typed number\n\n**Auth:** permission:computers.view\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/dashboard/sharedmachines\n```", + "summary": "Machine numbers claimed by more than one PC, with nothing filed under them.", + "description": "Machine numbers claimed by more than one PC, with nothing filed under them.\n\n**Auth:** permission:computers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/dashboard/sharedmachines -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -10460,13 +11677,13 @@ } } }, - "/api/computers/client-script": { + "/api/computers/dashboard/summary": { "get": { "tags": [ "plugin-computers" ], - "summary": "The collector reporter (Report-AssetToShopDB.ps1) stamped with THIS site's values: the site base URL becomes the...", - "description": "The collector reporter (Report-AssetToShopDB.ps1) stamped with THIS site's values: the site base URL becomes the -ApiUrl default and computers_routableranges becomes -AllowedRanges, so it downloads ready to deploy. Only the parameter DEFAULTS are substituted - the copy in plugins/computers/client/ stays runnable, so there is no second version to drift - and everything stamped stays overridable by argument or registry. The collector key is deliberately NOT included: this file lands on every shop-floor PC, and a token spread across hundreds of bays cannot be rotated quietly. Serves text/plain as an attachment, with the SHA-256 in X-Script-Sha256 so a deployment can verify what it fetched\n\n**Auth:** jwt + role:admin\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/client-script\n```", + "summary": "Get computer dashboard summary data.", + "description": "Get computer dashboard summary data.\n\n**Auth:** permission:computers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/dashboard/summary -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -10498,13 +11715,336 @@ } } }, + "/api/computers/display-kiosks": { + "get": { + "tags": [ + "plugin-computers" + ], + "summary": "Reporting display kiosks, for the Dashboard Defaults picker.", + "description": "Reporting display kiosks, for the Dashboard Defaults picker.\n\n**Auth:** permission:computers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/display-kiosks -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/computers/policies/actions": { + "get": { + "tags": [ + "plugin-computers" + ], + "summary": "The allowlisted action names + labels a policy may reference.", + "description": "The allowlisted action names + labels a policy may reference.\n\n**Auth:** permission:computers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/policies/actions -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/computers/policies/business": { + "get": { + "tags": [ + "plugin-computers" + ], + "summary": "UNDOCUMENTED: computers.list_business_policies has no docstring.", + "description": "UNDOCUMENTED: computers.list_business_policies has no docstring.\n\n**Auth:** permission:computers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/policies/business -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-computers" + ], + "summary": "UNDOCUMENTED: computers.create_business_policy has no docstring.", + "description": "UNDOCUMENTED: computers.create_business_policy has no docstring.\n\n**Auth:** permission:computers.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/computers/policies/business -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/computers/policies/business/{policy_id}": { + "delete": { + "tags": [ + "plugin-computers" + ], + "summary": "UNDOCUMENTED: computers.delete_business_policy has no docstring.", + "description": "UNDOCUMENTED: computers.delete_business_policy has no docstring.\n\n**Auth:** permission:computers.delete\n\n**Params:** path: policy_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/computers/policies/business/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "policy_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-computers" + ], + "summary": "UNDOCUMENTED: computers.get_business_policy has no docstring.", + "description": "UNDOCUMENTED: computers.get_business_policy has no docstring.\n\n**Auth:** permission:computers.view\n\n**Params:** path: policy_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/policies/business/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "policy_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-computers" + ], + "summary": "UNDOCUMENTED: computers.update_business_policy has no docstring.", + "description": "UNDOCUMENTED: computers.update_business_policy has no docstring.\n\n**Auth:** permission:computers.edit\n\n**Params:** path: policy_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/computers/policies/business/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "policy_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: policy_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, "/api/computers/policies/configuration": { "get": { "tags": [ "plugin-computers" ], - "summary": "List Configuration Policies (scope + the actions applied to matching endpoints); 404 while the...", - "description": "List Configuration Policies (scope + the actions applied to matching endpoints); 404 while the computers_configpolicies_enabled setting is off\n\n**Auth:** computers.view\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/configuration\n```", + "summary": "UNDOCUMENTED: computers.list_configuration_policies has no docstring.", + "description": "UNDOCUMENTED: computers.list_configuration_policies has no docstring.\n\n**Auth:** permission:computers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/policies/configuration -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -10539,8 +12079,8 @@ "tags": [ "plugin-computers" ], - "summary": "Create a Configuration Policy. Scope trees go through validate_scope and every action name is checked against the...", - "description": "Create a Configuration Policy. Scope trees go through validate_scope and every action name is checked against the endpoints command allowlist, so a bad tree or unknown action is rejected before it reaches the database; 404 while the computers_configpolicies_enabled setting is off\n\n**Auth:** computers.create\n\n**Params:** body: name (required), description, scopejson (scope AST), actions[] of {action, mode: desired|gate, params}\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"name\":\"Shopfloor MTConnect\",\"actions\":[{\"action\":\"mtconnect_start\",\"mode\":\"desired\"}]}' http://localhost:5001/api/computers/policies/configuration\n```", + "summary": "UNDOCUMENTED: computers.create_configuration_policy has no docstring.", + "description": "UNDOCUMENTED: computers.create_configuration_policy has no docstring.\n\n**Auth:** permission:computers.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/computers/policies/configuration -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -10577,7 +12117,7 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: name (required), description, scopejson (scope AST), actions[] of {action, mode: desired|gate, params}" + "description": "body: JSON (fields not yet documented)" } } } @@ -10585,12 +12125,12 @@ } }, "/api/computers/policies/configuration/{policy_id}": { - "get": { + "delete": { "tags": [ "plugin-computers" ], - "summary": "One Configuration Policy with its actions expanded; 404 while the computers_configpolicies_enabled setting is off", - "description": "One Configuration Policy with its actions expanded; 404 while the computers_configpolicies_enabled setting is off\n\n**Auth:** computers.view\n\n**Params:** path: policy_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/configuration/1policy_id\n```", + "summary": "UNDOCUMENTED: computers.delete_configuration_policy has no docstring.", + "description": "UNDOCUMENTED: computers.delete_configuration_policy has no docstring.\n\n**Auth:** permission:computers.delete\n\n**Params:** path: policy_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/computers/policies/configuration/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -10629,7 +12169,56 @@ "in": "path", "required": true, "schema": { - "type": "string" + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-computers" + ], + "summary": "UNDOCUMENTED: computers.get_configuration_policy has no docstring.", + "description": "UNDOCUMENTED: computers.get_configuration_policy has no docstring.\n\n**Auth:** permission:computers.view\n\n**Params:** path: policy_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/policies/configuration/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "policy_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" } } ] @@ -10638,8 +12227,8 @@ "tags": [ "plugin-computers" ], - "summary": "Update a Configuration Policy; same scope and action validation as create; 404 while the...", - "description": "Update a Configuration Policy; same scope and action validation as create; 404 while the computers_configpolicies_enabled setting is off\n\n**Auth:** computers.edit\n\n**Params:** path: policy_id; body: any of name, description, scopejson, actions[] (actions replace wholesale)\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/configuration/1policy_id\n```", + "summary": "UNDOCUMENTED: computers.update_configuration_policy has no docstring.", + "description": "UNDOCUMENTED: computers.update_configuration_policy has no docstring.\n\n**Auth:** permission:computers.edit\n\n**Params:** path: policy_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/computers/policies/configuration/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -10678,7 +12267,7 @@ "in": "path", "required": true, "schema": { - "type": "string" + "type": "integer" } } ], @@ -10689,345 +12278,11 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "path: policy_id; body: any of name, description, scopejson, actions[] (actions replace wholesale)" + "description": "path: policy_id (int); body: JSON (fields not yet documented)" } } } } - }, - "delete": { - "tags": [ - "plugin-computers" - ], - "summary": "Delete a Configuration Policy and its actions; 404 while the computers_configpolicies_enabled setting is off", - "description": "Delete a Configuration Policy and its actions; 404 while the computers_configpolicies_enabled setting is off\n\n**Auth:** computers.delete\n\n**Params:** path: policy_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/configuration/1policy_id\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "policy_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/computers/policies/business": { - "get": { - "tags": [ - "plugin-computers" - ], - "summary": "List Business Policies (ordered bundles of Configuration Policies); 404 while the computers_configpolicies_enabled...", - "description": "List Business Policies (ordered bundles of Configuration Policies); 404 while the computers_configpolicies_enabled setting is off\n\n**Auth:** computers.view\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/business\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - }, - "post": { - "tags": [ - "plugin-computers" - ], - "summary": "Create a Business Policy. Every referenced Configuration Policy must exist; priority decides which policy wins when two...", - "description": "Create a Business Policy. Every referenced Configuration Policy must exist; priority decides which policy wins when two match the same endpoint; 404 while the computers_configpolicies_enabled setting is off\n\n**Auth:** computers.create\n\n**Params:** body: name (required), description, priority, items[] of {configurationpolicyid, sortorder}\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/business\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: name (required), description, priority, items[] of {configurationpolicyid, sortorder}" - } - } - } - } - } - }, - "/api/computers/policies/business/{policy_id}": { - "get": { - "tags": [ - "plugin-computers" - ], - "summary": "One Business Policy with its member Configuration Policies in sort order; 404 while the...", - "description": "One Business Policy with its member Configuration Policies in sort order; 404 while the computers_configpolicies_enabled setting is off\n\n**Auth:** computers.view\n\n**Params:** path: policy_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/business/1policy_id\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "policy_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "plugin-computers" - ], - "summary": "Update a Business Policy or reorder its members; 404 while the computers_configpolicies_enabled setting is off", - "description": "Update a Business Policy or reorder its members; 404 while the computers_configpolicies_enabled setting is off\n\n**Auth:** computers.edit\n\n**Params:** path: policy_id; body: any of name, description, priority, items[] (items replace wholesale)\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/business/1policy_id\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "policy_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: policy_id; body: any of name, description, priority, items[] (items replace wholesale)" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-computers" - ], - "summary": "Delete a Business Policy and its item rows; 404 while the computers_configpolicies_enabled setting is off", - "description": "Delete a Business Policy and its item rows; 404 while the computers_configpolicies_enabled setting is off\n\n**Auth:** computers.delete\n\n**Params:** path: policy_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/business/1policy_id\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "policy_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/computers/policies/actions": { - "get": { - "tags": [ - "plugin-computers" - ], - "summary": "The allowlisted action names and display labels a policy may reference, read from the endpoints command allowlist. The...", - "description": "The allowlisted action names and display labels a policy may reference, read from the endpoints command allowlist. The UI builds its action picker from this rather than hardcoding a list that would drift; 404 while the computers_configpolicies_enabled setting is off\n\n**Auth:** computers.view\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/actions\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } } }, "/api/computers/policies/effective/{asset_id}": { @@ -11035,8 +12290,8 @@ "tags": [ "plugin-computers" ], - "summary": "Preview the resolved effective policy for one endpoint: which Business Policy won, which Configuration Policies...", - "description": "Preview the resolved effective policy for one endpoint: which Business Policy won, which Configuration Policies matched, and the resulting desired/gate actions. Read-only, for debugging a scope that is not matching what its author expected; 404 while the computers_configpolicies_enabled setting is off\n\n**Auth:** computers.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/effective/1asset_id\n```", + "summary": "Preview the resolved effective policy for one endpoint (debug/UI).", + "description": "Preview the resolved effective policy for one endpoint (debug/UI).\n\n**Auth:** permission:computers.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/policies/effective/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -11075,19 +12330,1143 @@ "in": "path", "required": true, "schema": { - "type": "string" + "type": "integer" } } ] } }, - "/api/computers/asset/{asset_id}/policypanel": { + "/api/computers/protocols": { "get": { "tags": [ "plugin-computers" ], - "summary": "Asset-detail panel: which policy governs this endpoint, how many configuration policies folded into it, and how many...", - "description": "Asset-detail panel: which policy governs this endpoint, how many configuration policies folded into it, and how many actions it allows or drives to a desired state. Provenance rather than a control - the effective policy narrows the command allowlist, so this is the answer to why an action is unavailable on a given PC. Returns an empty field list (not the feature-gate 404) when the policy feature is off, so the card self-hides instead of erroring on every site that has not enabled it\n\n**Auth:** computers.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/asset/1/policypanel\n```", + "summary": "List access protocols. ?active=false includes disabled ones.", + "description": "List access protocols. ?active=false includes disabled ones.\n\n**Auth:** permission:computers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/protocols -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-computers" + ], + "summary": "UNDOCUMENTED: computers.create_protocol has no docstring.", + "description": "UNDOCUMENTED: computers.create_protocol has no docstring.\n\n**Auth:** permission:computers.edit\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/computers/protocols -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/computers/protocols/{protocol_id}": { + "delete": { + "tags": [ + "plugin-computers" + ], + "summary": "UNDOCUMENTED: computers.delete_protocol has no docstring.", + "description": "UNDOCUMENTED: computers.delete_protocol has no docstring.\n\n**Auth:** permission:computers.edit\n\n**Params:** path: protocol_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/computers/protocols/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "protocol_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "patch": { + "tags": [ + "plugin-computers" + ], + "summary": "UNDOCUMENTED: computers.update_protocol has no docstring.", + "description": "UNDOCUMENTED: computers.update_protocol has no docstring.\n\n**Auth:** permission:computers.edit\n\n**Params:** path: protocol_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PATCH http://localhost:5001/api/computers/protocols/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "protocol_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: protocol_id (int); body: JSON (fields not yet documented)" + } + } + } + } + }, + "put": { + "tags": [ + "plugin-computers" + ], + "summary": "UNDOCUMENTED: computers.update_protocol has no docstring.", + "description": "UNDOCUMENTED: computers.update_protocol has no docstring.\n\n**Auth:** permission:computers.edit\n\n**Params:** path: protocol_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PATCH http://localhost:5001/api/computers/protocols/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "protocol_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: protocol_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/computers/types": { + "get": { + "tags": [ + "plugin-computers" + ], + "summary": "List all computer types.", + "description": "List all computer types.\n\n**Auth:** permission:computers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/types -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-computers" + ], + "summary": "Create a new computer type.", + "description": "Create a new computer type.\n\n**Auth:** permission:computers.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/computers/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/computers/types/{type_id}": { + "delete": { + "tags": [ + "plugin-computers" + ], + "summary": "Delete a computer type. Refused if any PC still uses it.", + "description": "Delete a computer type. Refused if any PC still uses it.\n\n**Auth:** permission:computers.delete\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/computers/types/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-computers" + ], + "summary": "Get a single computer type.", + "description": "Get a single computer type.\n\n**Auth:** permission:computers.view\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/computers/types/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-computers" + ], + "summary": "Update a computer type.", + "description": "Update a computer type.\n\n**Auth:** permission:computers.edit\n\n**Params:** path: type_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/computers/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: type_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/employees/{sso}/photo": { + "delete": { + "tags": [ + "plugin-employees" + ], + "summary": "Clear an employee photo and delete the uploaded file. External mode 409s.", + "description": "Clear an employee photo and delete the uploaded file. External mode 409s.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: sso (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/employees/1/photo -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "sso", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "post": { + "tags": [ + "plugin-employees" + ], + "summary": "Upload (or replace) the photo for a self-hosted directory employee.", + "description": "Upload (or replace) the photo for a self-hosted directory employee.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: sso (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/employees/1/photo -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "sso", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: sso (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/employees/directory": { + "get": { + "tags": [ + "plugin-employees" + ], + "summary": "Full self-hosted directory (for the management page).", + "description": "Full self-hosted directory (for the management page).\n\n**Auth:** permission:employees.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/employees/directory -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-employees" + ], + "summary": "UNDOCUMENTED: employees.create_directory_employee has no docstring.", + "description": "UNDOCUMENTED: employees.create_directory_employee has no docstring.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/employees/directory -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/employees/directory/{sso}": { + "delete": { + "tags": [ + "plugin-employees" + ], + "summary": "UNDOCUMENTED: employees.delete_directory_employee has no docstring.", + "description": "UNDOCUMENTED: employees.delete_directory_employee has no docstring.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: sso (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/employees/directory/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "sso", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-employees" + ], + "summary": "UNDOCUMENTED: employees.update_directory_employee has no docstring.", + "description": "UNDOCUMENTED: employees.update_directory_employee has no docstring.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: sso (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/employees/directory/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "sso", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: sso (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/employees/directory/import": { + "post": { + "tags": [ + "plugin-employees" + ], + "summary": "Bulk upsert from CSV. Accepts headers SSO,First_Name,Last_Name,Team,Role, Picture (case-insensitive; sso/firstname...", + "description": "Bulk upsert from CSV. Accepts headers SSO,First_Name,Last_Name,Team,Role, Picture (case-insensitive; sso/firstname/... also accepted).\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/employees/directory/import -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/employees/lookup": { + "get": { + "tags": [ + "plugin-employees" + ], + "summary": "Look up multiple employees by SSO list.", + "description": "Look up multiple employees by SSO list.\n\n**Auth:** permission:employees.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/employees/lookup -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/employees/lookup/{sso}": { + "get": { + "tags": [ + "plugin-employees" + ], + "summary": "Look up a single employee by SSO.", + "description": "Look up a single employee by SSO.\n\n**Auth:** permission:employees.view\n\n**Params:** path: sso (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/employees/lookup/x -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "sso", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ] + } + }, + "/api/employees/photo/{filename}": { + "get": { + "tags": [ + "plugin-employees" + ], + "summary": "Serve an uploaded employee photo (public - kiosk cards read it).", + "description": "Serve an uploaded employee photo (public - kiosk cards read it).\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** path: filename (path)\n\n**Example:**\n```\ncurl http://localhost:5001/api/employees/photo/example.png\n```", + "security": [], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "filename", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "may contain slashes" + } + } + ] + } + }, + "/api/employees/resolve/{sso}": { + "get": { + "tags": [ + "plugin-employees" + ], + "summary": "Resolve one SSO to a name and photo, and say HOW.", + "description": "Resolve one SSO to a name and photo, and say HOW.\n\n**Auth:** permission:employees.view\n\n**Params:** path: sso (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/employees/resolve/x -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "sso", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ] + } + }, + "/api/employees/search": { + "get": { + "tags": [ + "plugin-employees" + ], + "summary": "Search employees by name.", + "description": "Search employees by name.\n\n**Auth:** permission:employees.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/employees/search -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "List reported endpoint states.", + "description": "List reported endpoint states.\n\n**Auth:** permission:endpoints.fleet\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/ -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/{asset_id}": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Get the current state for one endpoint by asset id.", + "description": "Get the current state for one endpoint by asset id.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -11126,50 +13505,19 @@ "in": "path", "required": true, "schema": { - "type": "string" + "type": "integer" } } ] } }, - "/api/measuringtools/types": { - "get": { - "tags": [ - "plugin-measuringtools" - ], - "summary": "List measuring-tool types (active-only by default), paginated, name-sorted.", - "description": "List measuring-tool types (active-only by default), paginated, name-sorted.\n\n**Auth:** jwt-optional\n\n**Params:** query: active (default true; 'false' includes inactive), search (name ilike), page, perpage\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/measuringtools/types?search=caliper&page=1&perpage=25'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, + "/api/endpoints/{asset_id}/agentupgrade": { "post": { "tags": [ - "plugin-measuringtools" + "plugin-endpoints" ], - "summary": "Create a measuring-tool type; reactivates a soft-deleted same-named one, 409 if an active one exists.", - "description": "Create a measuring-tool type; reactivates a soft-deleted same-named one, 409 if an active one exists.\n\n**Auth:** jwt + permission:measuringtools.create\n\n**Params:** body JSON: name (required), description, color\n\n**Example:**\n```\ncurl -X POST 'http://localhost:5001/api/measuringtools/types' -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"name\":\"Thread Gage\",\"description\":\"Go/no-go thread gages\",\"color\":\"#4caf50\"}'\n```", + "summary": "Push the published agent build to one endpoint.", + "description": "Push the published agent build to one endpoint.\n\n**Auth:** permission:endpoints.agentupdate\n\n**Params:** path: asset_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/1/agentupgrade -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -11197,444 +13545,6 @@ }, "403": { "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body JSON: name (required), description, color" - } - } - } - } - } - }, - "/api/measuringtools/types/{type_id}": { - "get": { - "tags": [ - "plugin-measuringtools" - ], - "summary": "Get one measuring-tool type by id (404 if missing).", - "description": "Get one measuring-tool type by id (404 if missing).\n\n**Auth:** jwt-optional\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/measuringtools/types/3'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "plugin-measuringtools" - ], - "summary": "Update a measuring-tool type; 409 on rename collision with an existing name.", - "description": "Update a measuring-tool type; 409 on rename collision with an existing name.\n\n**Auth:** jwt + permission:measuringtools.edit\n\n**Params:** path: type_id; body JSON: name, description, color, isactive (only keys present are applied)\n\n**Example:**\n```\ncurl -X PUT 'http://localhost:5001/api/measuringtools/types/3' -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"description\":\"Updated\",\"isactive\":true}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: type_id; body JSON: name, description, color, isactive (only keys present are applied)" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-measuringtools" - ], - "summary": "Hard-delete a measuring-tool type; refused with 409 if any tool still references it.", - "description": "Hard-delete a measuring-tool type; refused with 409 if any tool still references it.\n\n**Auth:** jwt + permission:measuringtools.delete\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl -X DELETE 'http://localhost:5001/api/measuringtools/types/3' -H \"Authorization: Bearer $TOKEN\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/measuringtools": { - "get": { - "tags": [ - "plugin-measuringtools" - ], - "summary": "List measuring tools (Asset core merged with extension), filtered + paginated; derived calibrationstatus filter applied...", - "description": "List measuring tools (Asset core merged with extension), filtered + paginated; derived calibrationstatus filter applied post-pagination.\n\n**Auth:** jwt-optional\n\n**Params:** query: active (default true), assetnumber (exact match, for idempotent import), search (assetnumber/name/serialnumber/gaugelabreference/maintenancereference ilike), typeid, locationid, statusid, calibrationstatus (overdue|duesoon|current|unknown), page, perpage\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/measuringtools?typeid=2&calibrationstatus=overdue&page=1&perpage=50'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "plugin-measuringtools" - ], - "summary": "Create a measuring tool: one Asset core row (assettype 'measuring_tool') plus one measuringtools extension row in a...", - "description": "Create a measuring tool: one Asset core row (assettype 'measuring_tool') plus one measuringtools extension row in a single payload; audit-logged.\n\n**Auth:** jwt + permission:measuringtools.create\n\n**Params:** body JSON: assetnumber (required, 409 on duplicate), name, gaugelabreference, maintenancereference, serialnumber, statusid (default 1), locationid, businessunitid, mapx, mapy, notes, measuringtooltypeid, calibrationintervaldays, lastcalibrationdate (YYYY-MM-DD), nextcalibrationdate (YYYY-MM-DD), calibrationprovider; import timestamps honored via X-Import-Mode\n\n**Example:**\n```\ncurl -X POST 'http://localhost:5001/api/measuringtools' -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"assetnumber\":\"MT-0042\",\"name\":\"6in Digital Caliper\",\"measuringtooltypeid\":2,\"calibrationintervaldays\":365,\"lastcalibrationdate\":\"2026-01-15\",\"nextcalibrationdate\":\"2027-01-15\",\"calibrationprovider\":\"Gage Lab\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body JSON: assetnumber (required, 409 on duplicate), name, gaugelabreference, maintenancereference, serialnumber, statusid (default 1), locationid, businessunitid, mapx, mapy, notes, measuringtooltypeid, calibrationintervaldays, lastcalibrationdate (YYYY-MM-DD), nextcalibrationdate (YYYY-MM-DD), calibrationprovider; import timestamps honored via X-Import-Mode" - } - } - } - } - } - }, - "/api/measuringtools/{tool_id}": { - "get": { - "tags": [ - "plugin-measuringtools" - ], - "summary": "Get one measuring tool by measuringtoolid, asset core dict with extension nested under 'measuringtool'.", - "description": "Get one measuring tool by measuringtoolid, asset core dict with extension nested under 'measuringtool'.\n\n**Auth:** jwt-optional\n\n**Params:** path: tool_id (int)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/measuringtools/17'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "tool_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "plugin-measuringtools" - ], - "summary": "Update asset core fields and extension fields in one payload; 409 on assetnumber collision; changes audit-logged.", - "description": "Update asset core fields and extension fields in one payload; 409 on assetnumber collision; changes audit-logged.\n\n**Auth:** jwt + permission:measuringtools.edit\n\n**Params:** path: tool_id; body JSON (only present keys applied): asset fields assetnumber, name, gaugelabreference, maintenancereference, serialnumber, statusid, locationid, businessunitid, mapx, mapy, notes, isactive; extension fields measuringtooltypeid, calibrationintervaldays, calibrationprovider, notes, lastcalibrationdate, nextcalibrationdate (YYYY-MM-DD)\n\n**Example:**\n```\ncurl -X PUT 'http://localhost:5001/api/measuringtools/17' -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"locationid\":5,\"nextcalibrationdate\":\"2026-12-01\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "tool_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: tool_id; body JSON (only present keys applied): asset fields assetnumber, name, gaugelabreference, maintenancereference, serialnumber, statusid, locationid, businessunitid, mapx, mapy, notes, isactive; extension fields measuringtooltypeid, calibrationintervaldays, calibrationprovider, notes, lastcalibrationdate, nextcalibrationdate (YYYY-MM-DD)" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-measuringtools" - ], - "summary": "Soft-delete a measuring tool by setting its asset isactive=false; audit-logged.", - "description": "Soft-delete a measuring tool by setting its asset isactive=false; audit-logged.\n\n**Auth:** jwt + permission:measuringtools.delete\n\n**Params:** path: tool_id (int)\n\n**Example:**\n```\ncurl -X DELETE 'http://localhost:5001/api/measuringtools/17' -H \"Authorization: Bearer $TOKEN\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "tool_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/measuringtools/by-asset/{asset_id}": { - "get": { - "tags": [ - "plugin-measuringtools" - ], - "summary": "Get a measuring tool by its core assetid (404 if the asset has no extension row).", - "description": "Get a measuring tool by its core assetid (404 if the asset has no extension row).\n\n**Auth:** jwt-optional\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/measuringtools/by-asset/1042'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } }, "404": { "description": "No such record." @@ -11646,249 +13556,7 @@ "in": "path", "required": true, "schema": { - "type": "string" - } - } - ] - } - }, - "/api/measuringtools/map-overlay": { - "get": { - "tags": [ - "plugin-measuringtools" - ], - "summary": "ADR-010 map overlay: per-asset calibration badges [{assetid, color, label}] for active tools that are overdue or due...", - "description": "ADR-010 map overlay: per-asset calibration badges [{assetid, color, label}] for active tools that are overdue or due soon only; no coordinates returned.\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/measuringtools/map-overlay'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/measuringtools/report/calibration": { - "get": { - "tags": [ - "plugin-measuringtools" - ], - "summary": "Calibration report for the Reports hub: counts and full tool lists bucketed by derived status...", - "description": "Calibration report for the Reports hub: counts and full tool lists bucketed by derived status (overdue/duesoon/current/unknown) plus statuscolors map.\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/measuringtools/report/calibration'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/machines/types": { - "get": { - "tags": [ - "plugin-machines" - ], - "summary": "List machine types (active by default) with pagination and name search", - "description": "List machine types (active by default) with pagination and name search\n\n**Auth:** jwt-optional\n\n**Params:** query: page, per_page, active (pass 'false' to include inactive), search (ilike on machinetype)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/machines/types?search=cnc&page=1&per_page=25'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "plugin-machines" - ], - "summary": "Create a machine type; reactivates a soft-deleted type of the same name instead of 409ing", - "description": "Create a machine type; reactivates a soft-deleted type of the same name instead of 409ing\n\n**Auth:** jwt + permission:machines.create\n\n**Params:** body: machinetype (required), description, icon, color\n\n**Example:**\n```\ncurl -X POST 'http://localhost:5001/api/machines/types' -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"machinetype\":\"Lathe\",\"description\":\"Turning machines\",\"icon\":\"mdi-rotate-3d\",\"color\":\"#1976d2\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: machinetype (required), description, icon, color" - } - } - } - } - } - }, - "/api/machines/types/{type_id}": { - "get": { - "tags": [ - "plugin-machines" - ], - "summary": "Get a single machine type by ID", - "description": "Get a single machine type by ID\n\n**Auth:** jwt-optional\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/machines/types/3'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "plugin-machines" - ], - "summary": "Update a machine type (rename guarded by 409 on duplicate name)", - "description": "Update a machine type (rename guarded by 409 on duplicate name)\n\n**Auth:** jwt + permission:machines.edit\n\n**Params:** path: type_id; body: any of machinetype, description, icon, color, isactive\n\n**Example:**\n```\ncurl -X PUT 'http://localhost:5001/api/machines/types/3' -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"description\":\"5-axis mills\",\"isactive\":true}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "string" + "type": "integer" } } ], @@ -11899,194 +13567,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "path: type_id; body: any of machinetype, description, icon, color, isactive" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-machines" - ], - "summary": "Hard-delete a machine type; refused with 409 if any machine still references it", - "description": "Hard-delete a machine type; refused with 409 if any machine still references it\n\n**Auth:** jwt + permission:machines.delete\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl -X DELETE 'http://localhost:5001/api/machines/types/3' -H \"Authorization: Bearer $TOKEN\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/machines": { - "get": { - "tags": [ - "plugin-machines" - ], - "summary": "List machines (Asset+Machine join) with filters, sorting, pagination; collapses Dualpath dual-bay pairs to one row...", - "description": "List machines (Asset+Machine join) with filters, sorting, pagination; collapses Dualpath dual-bay pairs to one row (annotated with dualpathpartner) when site setting enabled\n\n**Auth:** jwt-optional\n\n**Params:** query: page, per_page, active ('false' includes inactive), assetnumber (exact-match for idempotent import), search (assetnumber/name/serialnumber/gaugelabreference/maintenancereference ilike), typeid|type_id, vendorid|vendor_id, locationid|location_id, businessunitid|businessunit_id, sort (assetnumber|name), dir (asc|desc)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/machines?search=2007&typeid=2&sort=name&dir=desc&page=1&per_page=50'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "plugin-machines" - ], - "summary": "Create a machine (creates both core Asset row and Machine extension row); audit-logged; honors X-Import-Mode legacy...", - "description": "Create a machine (creates both core Asset row and Machine extension row); audit-logged; honors X-Import-Mode legacy timestamps via apply_import_timestamps\n\n**Auth:** jwt + permission:machines.create\n\n**Params:** body: assetnumber (required, 409 on duplicate); asset fields: name, gaugelabreference, maintenancereference, serialnumber, statusid (default 1), locationid, businessunitid, mapx, mapy, notes; machine fields: machinetypeid, vendorid, modelnumberid, requiresmanualconfig, islocationonly, lastmaintenancedate, nextmaintenancedate, maintenanceintervaldays, controllervendorid, controllermodelid\n\n**Example:**\n```\ncurl -X POST 'http://localhost:5001/api/machines' -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"assetnumber\":\"2007\",\"name\":\"Makino a51\",\"machinetypeid\":2,\"vendorid\":5,\"locationid\":3,\"statusid\":1}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: assetnumber (required, 409 on duplicate); asset fields: name, gaugelabreference, maintenancereference, serialnumber, statusid (default 1), locationid, businessunitid, mapx, mapy, notes; machine fields: machinetypeid, vendorid, modelnumberid, requiresmanualconfig, islocationonly, lastmaintenancedate, nextmaintenancedate, maintenanceintervaldays, controllervendorid, controllermodelid" + "description": "path: asset_id (int); body: JSON (fields not yet documented)" } } } } } }, - "/api/machines/{machine_id}": { + "/api/endpoints/{asset_id}/commands": { "get": { "tags": [ - "plugin-machines" + "plugin-endpoints" ], - "summary": "Get one machine with full asset details, nested machine dict, and dualpathpartner info", - "description": "Get one machine with full asset details, nested machine dict, and dualpathpartner info\n\n**Auth:** jwt-optional\n\n**Params:** path: machine_id (int)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/machines/42'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "machine_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "plugin-machines" - ], - "summary": "Update machine (asset + machine fields), with per-field change tracking to AuditLog and 409 on assetnumber conflict", - "description": "Update machine (asset + machine fields), with per-field change tracking to AuditLog and 409 on assetnumber conflict\n\n**Auth:** jwt + permission:machines.edit\n\n**Params:** path: machine_id; body: any of assetnumber, name, gaugelabreference, maintenancereference, serialnumber, statusid, locationid, businessunitid, mapx, mapy, notes, isactive, machinetypeid, vendorid, modelnumberid, requiresmanualconfig, islocationonly, lastmaintenancedate, nextmaintenancedate, maintenanceintervaldays, controllervendorid, controllermodelid\n\n**Example:**\n```\ncurl -X PUT 'http://localhost:5001/api/machines/42' -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"locationid\":7,\"nextmaintenancedate\":\"2026-09-01\"}'\n```", + "summary": "Recent commands for one endpoint, newest first (operator view).", + "description": "Recent commands for one endpoint, newest first (operator view).\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/1/commands -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -12119,165 +13613,23 @@ "description": "No such record." } }, - "parameters": [ - { - "name": "machine_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: machine_id; body: any of assetnumber, name, gaugelabreference, maintenancereference, serialnumber, statusid, locationid, businessunitid, mapx, mapy, notes, isactive, machinetypeid, vendorid, modelnumberid, requiresmanualconfig, islocationonly, lastmaintenancedate, nextmaintenancedate, maintenanceintervaldays, controllervendorid, controllermodelid" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-machines" - ], - "summary": "Soft-delete a machine (sets asset.isactive=False, keeps Machine row linked); audit-logged", - "description": "Soft-delete a machine (sets asset.isactive=False, keeps Machine row linked); audit-logged\n\n**Auth:** jwt + permission:machines.delete\n\n**Params:** path: machine_id (int)\n\n**Example:**\n```\ncurl -X DELETE 'http://localhost:5001/api/machines/42' -H \"Authorization: Bearer $TOKEN\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "machine_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/machines/by-asset/{asset_id}": { - "get": { - "tags": [ - "plugin-machines" - ], - "summary": "Get machine data looked up by core asset ID instead of machine ID", - "description": "Get machine data looked up by core asset ID instead of machine ID\n\n**Auth:** jwt-optional\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/machines/by-asset/1001'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, "parameters": [ { "name": "asset_id", "in": "path", "required": true, "schema": { - "type": "string" + "type": "integer" } } ] - } - }, - "/api/machines/dashboard/summary": { - "get": { + }, + "post": { "tags": [ - "plugin-machines" + "plugin-endpoints" ], - "summary": "Dashboard summary: total active machine count plus counts grouped by machine type and by asset status (total and...", - "description": "Dashboard summary: total active machine count plus counts grouped by machine type and by asset status (total and by-type exclude Dualpath secondary bays when collapse setting enabled; by-status does not)\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/machines/dashboard/summary'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/machines/dashboard/outofservice": { - "get": { - "tags": [ - "plugin-machines" - ], - "summary": "Dashboard card: machines whose status says they are not running", - "description": "Dashboard card: machines whose status says they are not running\n\n**Auth:** permission:machines.view\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/machines/dashboard/outofservice\n```", + "summary": "Queue an allowlisted command for one endpoint.", + "description": "Queue an allowlisted command for one endpoint.\n\n**Auth:** permission:endpoints.command\n\n**Params:** path: asset_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/1/commands -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -12305,6 +13657,4371 @@ }, "403": { "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: asset_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/{asset_id}/events": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "List curated Windows events for one endpoint, newest first.", + "description": "List curated Windows events for one endpoint, newest first.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/1/events -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/{asset_id}/holdableactions": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Desired-mode actions on this endpoint, with current hold state.", + "description": "Desired-mode actions on this endpoint, with current hold state.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/1/holdableactions -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/{asset_id}/lock": { + "delete": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Lift the quarantine on this endpoint.", + "description": "Lift the quarantine on this endpoint.\n\n**Auth:** permission:endpoints.command\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/endpoints/1/lock -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "The endpoint lock standing on this endpoint, or null.", + "description": "The endpoint lock standing on this endpoint, or null.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/1/lock -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Quarantine this endpoint against non-admin commands.", + "description": "Quarantine this endpoint against non-admin commands.\n\n**Auth:** permission:endpoints.command\n\n**Params:** path: asset_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/1/lock -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: asset_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/{asset_id}/logcollections": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Collected-log history for one endpoint, newest first.", + "description": "Collected-log history for one endpoint, newest first.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/1/logcollections -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/{asset_id}/policyholds": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Active break-glass holds for one endpoint.", + "description": "Active break-glass holds for one endpoint.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/1/policyholds -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Deliberately suspend desired-state reconciliation for one or more actions.", + "description": "Deliberately suspend desired-state reconciliation for one or more actions.\n\n**Auth:** permission:endpoints.command\n\n**Params:** path: asset_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/1/policyholds -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: asset_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/{asset_id}/policyholds/{action}": { + "delete": { + "tags": [ + "plugin-endpoints" + ], + "summary": "End a hold early, resuming reconciliation of one action.", + "description": "End a hold early, resuming reconciliation of one action.\n\n**Auth:** permission:endpoints.command\n\n**Params:** path: asset_id (int), action (string)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/endpoints/1/policyholds/x -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "action", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ] + } + }, + "/api/endpoints/{asset_id}/scriptruns": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "The last few run_script commands for one endpoint, newest first.", + "description": "The last few run_script commands for one endpoint, newest first.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/1/scriptruns -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/adoptiontool/package": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Bundle the adoption tool as a downloadable ZIP.", + "description": "Bundle the adoption tool as a downloadable ZIP.\n\n**Auth:** permission:endpoints.agentpackage\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/adoptiontool/package -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/agentkey": { + "delete": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Revoke the endpoint agent key: stop all managed-token endpoint traffic.", + "description": "Revoke the endpoint agent key: stop all managed-token endpoint traffic.\n\n**Auth:** jwt + role:admin\n\n**Params:** not documented\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/endpoints/agentkey -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Report the endpoint agent key state (no secret).", + "description": "Report the endpoint agent key state (no secret).\n\n**Auth:** jwt + role:admin\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/agentkey -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/agentkey/regenerate": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Mint a fresh endpoint agent key, revoking any previous one.", + "description": "Mint a fresh endpoint agent key, revoking any previous one.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/agentkey/regenerate -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/alerts": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Fleet alerts for one endpoint by hostname (collector-token auth).", + "description": "Fleet alerts for one endpoint by hostname (collector-token auth).\n\n**Auth:** api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/alerts -H \"X-API-Key: $KEY\"\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/alerts/{notification_id}/acknowledge": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Agent acknowledges a fleet alert (collector-token auth).", + "description": "Agent acknowledges a fleet alert (collector-token auth).\n\n**Auth:** api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)\n\n**Params:** path: notification_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/alerts/1/acknowledge -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "notification_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: notification_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/apppackage/{appversionid}": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Agent-facing installer download (collector-token auth).", + "description": "Agent-facing installer download (collector-token auth).\n\n**Auth:** api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)\n\n**Params:** path: appversionid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/apppackage/1 -H \"X-API-Key: $KEY\"\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "appversionid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/apppackage/app/{app_id}": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Agent-facing installer download by APP (collector-token auth).", + "description": "Agent-facing installer download by APP (collector-token auth).\n\n**Auth:** api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)\n\n**Params:** path: app_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/apppackage/app/1 -H \"X-API-Key: $KEY\"\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/apppackage/app/{app_id}/{filename}": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Agent-facing installer download by APP (collector-token auth).", + "description": "Agent-facing installer download by APP (collector-token auth).\n\n**Auth:** api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)\n\n**Params:** path: app_id (int), filename (path)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/apppackage/app/1/example.png -H \"X-API-Key: $KEY\"\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "filename", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "may contain slashes" + } + } + ] + } + }, + "/api/endpoints/asset/{asset_id}/activity": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "List panel: one chronological feed of what happened to this endpoint.", + "description": "List panel: one chronological feed of what happened to this endpoint.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/asset/1/activity -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/asset/{asset_id}/applications": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Table panel: the installed-software (SBOM) snapshot for this endpoint.", + "description": "Table panel: the installed-software (SBOM) snapshot for this endpoint.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/asset/1/applications -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/asset/{asset_id}/diskhealth": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Physical drive health rows.", + "description": "Physical drive health rows.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/asset/1/diskhealth -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/asset/{asset_id}/hardware": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Keyvalue panel: processor, memory and system chassis inventory.", + "description": "Keyvalue panel: processor, memory and system chassis inventory.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/asset/1/hardware -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/asset/{asset_id}/interfaces": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Table panel: every hardware NIC the agent last reported.", + "description": "Table panel: every hardware NIC the agent last reported.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/asset/1/interfaces -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/asset/{asset_id}/iphistory": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Table panel: every address this asset has held, newest change first.", + "description": "Table panel: every address this asset has held, newest change first.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/asset/1/iphistory -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/asset/{asset_id}/listeners": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Table panel: TCP sockets in LISTEN, with the owning process.", + "description": "Table panel: TCP sockets in LISTEN, with the owning process.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/asset/1/listeners -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/asset/{asset_id}/mappeddrives": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "List panel: mapped network drives with a health badge each.", + "description": "List panel: mapped network drives with a health badge each.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/asset/1/mappeddrives -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/asset/{asset_id}/osinfo": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Keyvalue panel: operating system, firmware and domain membership.", + "description": "Keyvalue panel: operating system, firmware and domain membership.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/asset/1/osinfo -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/asset/{asset_id}/recentevents": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "List panel: the endpoint's most recent curated events, newest first.", + "description": "List panel: the endpoint's most recent curated events, newest first.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/asset/1/recentevents -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/asset/{asset_id}/services": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "List panel: monitored services/processes with a run-state badge per row.", + "description": "List panel: monitored services/processes with a run-state badge per row.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/asset/1/services -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/asset/{asset_id}/status": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Keyvalue panel: the endpoint's current status snapshot.", + "description": "Keyvalue panel: the endpoint's current status snapshot.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/asset/1/status -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/asset/{asset_id}/storage": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Tabs panel: volume capacity and physical drive health as two sections.", + "description": "Tabs panel: volume capacity and physical drive health as two sections.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/asset/1/storage -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/asset/{asset_id}/telemetrymeta": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Lightweight freshness probe for the panels.", + "description": "Lightweight freshness probe for the panels.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/asset/1/telemetrymeta -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/asset/{asset_id}/usercontext": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Keyvalue panel: session-scoped data from the interactive user probe.", + "description": "Keyvalue panel: session-scoped data from the interactive user probe.\n\n**Auth:** permission:endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/asset/1/usercontext -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/collect": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Agent telemetry ingest. Same handler as POST /api/collector/endpoints.", + "description": "Agent telemetry ingest. Same handler as POST /api/collector/endpoints.\n\n**Auth:** api-key (endpoints collector token)\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/collect -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/commands/{command_id}/result": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Agent reports a claimed command's outcome (collector-token auth).", + "description": "Agent reports a claimed command's outcome (collector-token auth).\n\n**Auth:** api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)\n\n**Params:** path: command_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/commands/1/result -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "command_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: command_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/commands/next": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Agent long-poll: claim the next pending command for a hostname.", + "description": "Agent long-poll: claim the next pending command for a hostname.\n\n**Auth:** api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/commands/next -H \"X-API-Key: $KEY\"\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/dashboard/enrollmentpending": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "One queue row while enroll requests wait for an admin, else nothing.", + "description": "One queue row while enroll requests wait for an admin, else nothing.\n\n**Auth:** permission:endpoints.fleet\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/dashboard/enrollmentpending -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/dashboard/quiet": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Endpoints that reported before, then went quiet past the window.", + "description": "Endpoints that reported before, then went quiet past the window.\n\n**Auth:** permission:endpoints.fleet\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/dashboard/quiet -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/dashboard/reenrollblocked": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "PCs kept from re-enrolling for more than 7 days.", + "description": "PCs kept from re-enrolling for more than 7 days.\n\n**Auth:** permission:endpoints.fleet\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/dashboard/reenrollblocked -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/dashboard/stability": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Endpoints with recent crash/shutdown/corruption events.", + "description": "Endpoints with recent crash/shutdown/corruption events.\n\n**Auth:** permission:endpoints.fleet\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/dashboard/stability -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/dashboard/summary": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "High-level fleet counts for the endpoints dashboard.", + "description": "High-level fleet counts for the endpoints dashboard.\n\n**Auth:** permission:endpoints.fleet\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/dashboard/summary -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/enroll": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "A PC asks for its own credential.", + "description": "A PC asks for its own credential.\n\n**Auth:** none (per-IP rate limited and capped; a valid fleet key only decides auto-enroll)\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/enroll -H 'Content-Type: application/json' -d '{}'\n```", + "security": [], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/enrollment": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Credential state of every computer, plus pending and recent requests.", + "description": "Credential state of every computer, plus pending and recent requests.\n\n**Auth:** permission:endpoints.fleet\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/enrollment -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/enrollment/assets/{assetid}/allow-reenroll": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Lift a re-enrollment block. The PC enrolls on its next attempt.", + "description": "Lift a re-enrollment block. The PC enrolls on its next attempt.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: assetid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/enrollment/assets/1/allow-reenroll -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "assetid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: assetid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/enrollment/assets/{assetid}/revoke": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Un-enroll a PC. Its agent gets CREDENTIAL_INVALID and enrolls again.", + "description": "Un-enroll a PC. Its agent gets CREDENTIAL_INVALID and enrolls again.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: assetid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/enrollment/assets/1/revoke -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "assetid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: assetid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/enrollment/migration": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Per-computer readiness to move onto per-PC credentials, plus waves.", + "description": "Per-computer readiness to move onto per-PC credentials, plus waves.\n\n**Auth:** permission:endpoints.fleet\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/enrollment/migration -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/enrollment/migration/wave": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Push the agent upgrade to a batch of READY PCs.", + "description": "Push the agent upgrade to a batch of READY PCs.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/enrollment/migration/wave -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/enrollment/requests/{requestid}/approve": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Bind a pending request to the asset the admin picked.", + "description": "Bind a pending request to the asset the admin picked.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: requestid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/enrollment/requests/1/approve -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "requestid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: requestid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/enrollment/requests/{requestid}/reject": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "UNDOCUMENTED: endpoints.reject_enroll_request has no docstring.", + "description": "UNDOCUMENTED: endpoints.reject_enroll_request has no docstring.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: requestid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/enrollment/requests/1/reject -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "requestid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: requestid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/enrollment/settings": { + "put": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Change the enrollment settings.", + "description": "Change the enrollment settings.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/endpoints/enrollment/settings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/logcollections/{collection_id}": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "One collected log including its content, for the scrollable pane.", + "description": "One collected log including its content, for the scrollable pane.\n\n**Auth:** permission:endpoints.updateapp\n\n**Params:** path: collection_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/logcollections/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "collection_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/logcollections/{collection_id}/download": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Serve a collected log as a file attachment.", + "description": "Serve a collected log as a file attachment.\n\n**Auth:** permission:endpoints.updateapp\n\n**Params:** path: collection_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/logcollections/1/download -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "collection_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/monitors": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "List monitor definitions (shipped and local).", + "description": "List monitor definitions (shipped and local).\n\n**Auth:** permission:endpoints.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/monitors -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Create a user-defined (local) monitor definition.", + "description": "Create a user-defined (local) monitor definition.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/monitors -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/monitors/{monitor_id}": { + "delete": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Delete a local monitor definition. Shipped rows are protected.", + "description": "Delete a local monitor definition. Shipped rows are protected.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: monitor_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/endpoints/monitors/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "monitor_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Update a local monitor definition. Shipped rows are read-only.", + "description": "Update a local monitor definition. Shipped rows are read-only.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: monitor_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/endpoints/monitors/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "monitor_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: monitor_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/monitors/resolve": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Resolve the monitor set for a PC type (agent runtime fetch).", + "description": "Resolve the monitor set for a PC type (agent runtime fetch).\n\n**Auth:** permission:endpoints.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/monitors/resolve -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/monitors/resolved": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Agent watch list: the monitors this endpoint should report on.", + "description": "Agent watch list: the monitors this endpoint should report on.\n\n**Auth:** api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/monitors/resolved -H \"X-API-Key: $KEY\"\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/operatorreports": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Agent uploads one operator issue report (collector-token auth).", + "description": "Agent uploads one operator issue report (collector-token auth).\n\n**Auth:** api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/operatorreports -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/operatorreports/{operatorreportid}/image": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Serve one operator report's screenshot.", + "description": "Serve one operator report's screenshot.\n\n**Auth:** permission:endpoints.operatorreports\n\n**Params:** path: operatorreportid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/operatorreports/1/image -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "operatorreportid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/panel/{asset_id}/operatorreports": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Operator Reports panel: this PC's recent operator issue reports.", + "description": "Operator Reports panel: this PC's recent operator issue reports.\n\n**Auth:** permission:endpoints.operatorreports\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/panel/1/operatorreports -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/scriptbody/{script_id}": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Agent-facing script body download (collector-token auth).", + "description": "Agent-facing script body download (collector-token auth).\n\n**Auth:** api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)\n\n**Params:** path: script_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/scriptbody/1 -H \"X-API-Key: $KEY\"\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "script_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/endpoints/settings/server-cert": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Get details about the current server certificate.", + "description": "Get details about the current server certificate.\n\n**Auth:** jwt + role:admin\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/settings/server-cert -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/settings/server-cert/download": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "The PUBLIC server certificate as a DER .cer file, never the key.", + "description": "The PUBLIC server certificate as a DER .cer file, never the key.\n\n**Auth:** permission:endpoints.fleet\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/settings/server-cert/download -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/settings/server-cert/generate": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Generate a new self-signed server certificate.", + "description": "Generate a new self-signed server certificate.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/settings/server-cert/generate -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/settings/server-cert/live": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "The certificate PCs actually receive from this site's public address.", + "description": "The certificate PCs actually receive from this site's public address.\n\n**Auth:** permission:endpoints.fleet\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/settings/server-cert/live -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/settings/server-cert/upload": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Upload a new server certificate and private key.", + "description": "Upload a new server certificate and private key.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/settings/server-cert/upload -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/siteagent": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Report whether the site agent build has a prebuilt binary staged.", + "description": "Report whether the site agent build has a prebuilt binary staged.\n\n**Auth:** permission:endpoints.agentpackage\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/siteagent -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/siteagent/binaries/{binkey}": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "List staged versions for a binary (agent or tray), newest first.", + "description": "List staged versions for a binary (agent or tray), newest first.\n\n**Auth:** permission:endpoints.agentpackage\n\n**Params:** path: binkey (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/siteagent/binaries/x -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "binkey", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Stage an uploaded binary as a new version.", + "description": "Stage an uploaded binary as a new version.\n\n**Auth:** permission:endpoints.agentpackage\n\n**Params:** path: binkey (string); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/x -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "binkey", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: binkey (string); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/siteagent/binaries/{binkey}/adopt": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Adopt the unmanaged legacy flat file into the version store.", + "description": "Adopt the unmanaged legacy flat file into the version store.\n\n**Auth:** permission:endpoints.agentpackage\n\n**Params:** path: binkey (string); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/x/adopt -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "binkey", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: binkey (string); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/siteagent/binaries/{binkey}/current": { + "put": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Point 'current' at an already-staged version (roll back or forward).", + "description": "Point 'current' at an already-staged version (roll back or forward).\n\n**Auth:** permission:endpoints.agentpackage\n\n**Params:** path: binkey (string); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/endpoints/siteagent/binaries/x/current -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "binkey", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: binkey (string); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/siteagent/binaries/{binkey}/detect": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Read the PE file version of an uploaded EXE without staging it.", + "description": "Read the PE file version of an uploaded EXE without staging it.\n\n**Auth:** permission:endpoints.agentpackage\n\n**Params:** path: binkey (string); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/x/detect -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "binkey", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: binkey (string); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/siteagent/bootstrap.ps1": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "The PowerShell the one-line command downloads, runs and deletes.", + "description": "The PowerShell the one-line command downloads, runs and deletes.\n\n**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)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/siteagent/bootstrap.ps1\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/siteagent/bootstrap/package": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "The keyless agent package the bootstrap script installs.", + "description": "The keyless agent package the bootstrap script installs.\n\n**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)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/siteagent/bootstrap/package\n```", + "security": [ + { + "apiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/endpoints/siteagent/bootstrap/tokens": { + "get": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Every bootstrap token, newest first. Never a secret.", + "description": "Every bootstrap token, newest first. Never a secret.\n\n**Auth:** jwt + role:admin\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/siteagent/bootstrap/tokens -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Mint one short-lived bootstrap token. The secret is returned ONCE.", + "description": "Mint one short-lived bootstrap token. The secret is returned ONCE.\n\n**Auth:** jwt + role:admin\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/siteagent/bootstrap/tokens -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/siteagent/bootstrap/tokens/{tokenid}/revoke": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Kill a bootstrap token now. Already-revoked is a no-op, not an error.", + "description": "Kill a bootstrap token now. Already-revoked is a no-op, not an error.\n\n**Auth:** jwt + role:admin\n\n**Params:** path: tokenid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/siteagent/bootstrap/tokens/1/revoke -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "tokenid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: tokenid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/endpoints/siteagent/package": { + "post": { + "tags": [ + "plugin-endpoints" + ], + "summary": "Bundle the site agent as a downloadable ZIP.", + "description": "Bundle the site agent as a downloadable ZIP.\n\n**Auth:** permission:endpoints.agentpackage\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/siteagent/package -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/geenforce/applications": { + "get": { + "tags": [ + "plugin-geenforce" + ], + "summary": "The core Applications catalog (id + name) for the curated entry->app link picker in the entry editor.", + "description": "The core Applications catalog (id + name) for the curated entry->app link picker in the entry editor.\n\n**Auth:** permission:geenforce.view or geenforce.manage\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/geenforce/applications -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/geenforce/config": { + "get": { + "tags": [ + "plugin-geenforce" + ], + "summary": "Plugin config: the on-share export root + the client IP allowlist.", + "description": "Plugin config: the on-share export root + the client IP allowlist.\n\n**Auth:** permission:geenforce.view or geenforce.manage\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/geenforce/config -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "put": { + "tags": [ + "plugin-geenforce" + ], + "summary": "UNDOCUMENTED: geenforce.put_config has no docstring.", + "description": "UNDOCUMENTED: geenforce.put_config has no docstring.\n\n**Auth:** permission:geenforce.publish\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/geenforce/config -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/geenforce/dashboard/failures": { + "get": { + "tags": [ + "plugin-geenforce" + ], + "summary": "Entries that FAILED on their PC's most recent enforcement cycle.", + "description": "Entries that FAILED on their PC's most recent enforcement cycle.\n\n**Auth:** permission:geenforce.view or geenforce.manage\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/geenforce/dashboard/failures -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/geenforce/entries/{entryid}": { + "delete": { + "tags": [ + "plugin-geenforce" + ], + "summary": "UNDOCUMENTED: geenforce.delete_entry has no docstring.", + "description": "UNDOCUMENTED: geenforce.delete_entry has no docstring.\n\n**Auth:** permission:geenforce.manage\n\n**Params:** path: entryid (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/geenforce/entries/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "entryid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-geenforce" + ], + "summary": "UNDOCUMENTED: geenforce.update_entry has no docstring.", + "description": "UNDOCUMENTED: geenforce.update_entry has no docstring.\n\n**Auth:** permission:geenforce.manage\n\n**Params:** path: entryid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/geenforce/entries/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "entryid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: entryid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/geenforce/entries/{entryid}/payload": { + "get": { + "tags": [ + "plugin-geenforce" + ], + "summary": "Return the stored inline payload bytes for an entry (404 if none).", + "description": "Return the stored inline payload bytes for an entry (404 if none).\n\n**Auth:** permission:geenforce.view or geenforce.manage\n\n**Params:** path: entryid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/geenforce/entries/1/payload -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "entryid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "post": { + "tags": [ + "plugin-geenforce" + ], + "summary": "Store an inline payload (<= 1 MB) for an entry and point the entry at it.", + "description": "Store an inline payload (<= 1 MB) for an entry and point the entry at it.\n\n**Auth:** permission:geenforce.publish\n\n**Params:** path: entryid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/geenforce/entries/1/payload -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "entryid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: entryid (int); body: JSON (fields not yet documented)" + } + } } } } @@ -12314,8 +18031,8 @@ "tags": [ "plugin-geenforce" ], - "summary": "Serve the current PUBLISHED manifest JSON snapshot for a scope (never the live draft) to the GE-Enforce client, with...", - "description": "Serve the current PUBLISHED manifest JSON snapshot for a scope (never the live draft) to the GE-Enforce client, with ETag/304 support and X-Manifest-Version header.\n\n**Auth:** api-key (managed service token with geenforce.fetch scope via X-API-Key or Bearer PAT) OR source IP in geenforce_allowed_cidrs setting; resource-bound tokens restricted to their listed scopes (403 otherwise)\n\n**Params:** query: pctype (required, =scopename), phase (default 'runtime'); header: If-None-Match for 304\n\n**Example:**\n```\ncurl -H \"X-API-Key: $TOKEN\" 'http://localhost:5001/api/geenforce/manifest?pctype=cmm&phase=runtime'\n```", + "summary": "Serve the current published manifest for a scope (fat-client: full scope).", + "description": "Serve the current published manifest for a scope (fat-client: full scope).\n\n**Auth:** api-key (geenforce fetch token, or the IP allowlist)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/geenforce/manifest -H \"X-API-Key: $KEY\"\n```", "security": [ { "apiKeyAuth": [] @@ -12352,8 +18069,8 @@ "tags": [ "plugin-geenforce" ], - "summary": "Download a payload blob by content hash (blob store first, then inline DB payload) so share-less PCs can pull...", - "description": "Download a payload blob by content hash (blob store first, then inline DB payload) so share-less PCs can pull installers over HTTPS; per-IP rate limited (120/60s default) and size-capped (512MB default, 413 above).\n\n**Auth:** api-key (geenforce.fetch service token) OR IP allowlist; resource-bound tokens get 404 for blobs not referenced by their scopes\n\n**Params:** path: sha256 (64 lowercase hex chars, 400 otherwise); header: If-None-Match (ETag = the hash)\n\n**Example:**\n```\ncurl -H \"X-API-Key: $TOKEN\" -o installer.exe 'http://localhost:5001/api/geenforce/payload/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'\n```", + "summary": "Serve a payload blob by content hash over HTTPS.", + "description": "Serve a payload blob by content hash over HTTPS.\n\n**Auth:** api-key (geenforce fetch token, or the IP allowlist)\n\n**Params:** path: sha256 (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/geenforce/payload/x -H \"X-API-Key: $KEY\"\n```", "security": [ { "apiKeyAuth": [] @@ -12403,8 +18120,8 @@ "tags": [ "plugin-geenforce" ], - "summary": "Record one PC's enforcement cycle: applied manifest version plus per-entry self-heal outcomes...", - "description": "Record one PC's enforcement cycle: applied manifest version plus per-entry self-heal outcomes (installed/skipped/failed); returns reportid + status.\n\n**Auth:** api-key (managed service token with geenforce.report scope) OR IP allowlist\n\n**Params:** JSON body: hostname (required); remainder parsed by service.record_enforcement_report (scopename, phase, appliedversion, enforcerversion, status, per-entry results, counts); 400 on ValueError\n\n**Example:**\n```\ncurl -X POST -H \"X-API-Key: $TOKEN\" -H 'Content-Type: application/json' -d '{\"hostname\":\"WKSTN0042\",\"scopename\":\"cmm\",\"appliedversion\":4,\"results\":[{\"entryname\":\"7zip\",\"action\":\"installed\"}]}' http://localhost:5001/api/geenforce/report\n```", + "summary": "Record one PC's enforcement cycle: applied version (did it receive the update?) + per-entry self-heal outcomes...", + "description": "Record one PC's enforcement cycle: applied version (did it receive the update?) + per-entry self-heal outcomes (installed/skipped/failed).\n\n**Auth:** api-key (geenforce report token)\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/geenforce/report -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "apiKeyAuth": [] @@ -12441,20 +18158,109 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "JSON body: hostname (required); remainder parsed by service.record_enforcement_report (scopename, phase, appliedversion, enforcerversion, status, per-entry results, counts); 400 on ValueError" + "description": "body: JSON (fields not yet documented)" } } } } } }, + "/api/geenforce/reports": { + "get": { + "tags": [ + "plugin-geenforce" + ], + "summary": "Latest enforcement report per PC (fleet compliance view).", + "description": "Latest enforcement report per PC (fleet compliance view).\n\n**Auth:** permission:geenforce.view or geenforce.manage\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/geenforce/reports -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/geenforce/reports/{reportid}": { + "get": { + "tags": [ + "plugin-geenforce" + ], + "summary": "One report with per-entry outcomes (self-heal / failure detail).", + "description": "One report with per-entry outcomes (self-heal / failure detail).\n\n**Auth:** permission:geenforce.view or geenforce.manage\n\n**Params:** path: reportid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/geenforce/reports/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "reportid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, "/api/geenforce/scopes": { "get": { "tags": [ "plugin-geenforce" ], - "summary": "List all imaging PC-type scopes with entry counts and current published version numbers, ordered by phase then...", - "description": "List all imaging PC-type scopes with entry counts and current published version numbers, ordered by phase then scopename.\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/scopes\n```", + "summary": "List imaging PC-type scopes with entry + published-version counts.", + "description": "List imaging PC-type scopes with entry + published-version counts.\n\n**Auth:** permission:geenforce.view or geenforce.manage\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/geenforce/scopes -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -12489,8 +18295,8 @@ "tags": [ "plugin-geenforce" ], - "summary": "Create a new manifest scope; 400 if scopename missing, phase invalid, or scope already exists for that scopename+phase...", - "description": "Create a new manifest scope; 400 if scopename missing, phase invalid, or scope already exists for that scopename+phase; returns 201 with scope summary.\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** JSON body: scopename (required), phase (default 'runtime', must be in PHASES), manifestversion (default '1.0'), description, computertypeid, measuringtooltypeid, iscommon (defaults true when scopename=='common')\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"scopename\":\"cmm\",\"phase\":\"runtime\",\"description\":\"CMM bays\"}' http://localhost:5001/api/geenforce/scopes\n```", + "summary": "UNDOCUMENTED: geenforce.create_scope has no docstring.", + "description": "UNDOCUMENTED: geenforce.create_scope has no docstring.\n\n**Auth:** permission:geenforce.manage\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/geenforce/scopes -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -12527,7 +18333,7 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "JSON body: scopename (required), phase (default 'runtime', must be in PHASES), manifestversion (default '1.0'), description, computertypeid, measuringtooltypeid, iscommon (defaults true when scopename=='common')" + "description": "body: JSON (fields not yet documented)" } } } @@ -12535,122 +18341,12 @@ } }, "/api/geenforce/scopes/{scopeid}": { - "get": { - "tags": [ - "plugin-geenforce" - ], - "summary": "Get one scope's summary plus its full draft entry list (each entry includes entryid, sortorder, curated appid/appname...", - "description": "Get one scope's summary plus its full draft entry list (each entry includes entryid, sortorder, curated appid/appname link, and inline-payload metadata).\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** path: scopeid\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/scopes/3\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "scopeid", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - }, - "put": { - "tags": [ - "plugin-geenforce" - ], - "summary": "Update scope metadata fields (only keys present in the body are changed); scopename and phase are immutable here.", - "description": "Update scope metadata fields (only keys present in the body are changed); scopename and phase are immutable here.\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** JSON body (all optional): description, computertypeid, measuringtooltypeid, manifestversion (stringified), iscommon (bool-coerced)\n\n**Example:**\n```\ncurl -X PUT -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"description\":\"updated\",\"manifestversion\":\"1.1\"}' http://localhost:5001/api/geenforce/scopes/3\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "scopeid", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "JSON body (all optional): description, computertypeid, measuringtooltypeid, manifestversion (stringified), iscommon (bool-coerced)" - } - } - } - } - }, "delete": { "tags": [ "plugin-geenforce" ], - "summary": "Delete a scope (and via cascade its entries); returns {deleted: scopeid}.", - "description": "Delete a scope (and via cascade its entries); returns {deleted: scopeid}.\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** path: scopeid\n\n**Example:**\n```\ncurl -X DELETE -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/scopes/3\n```", + "summary": "UNDOCUMENTED: geenforce.delete_scope has no docstring.", + "description": "UNDOCUMENTED: geenforce.delete_scope has no docstring.\n\n**Auth:** permission:geenforce.manage\n\n**Params:** path: scopeid (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/geenforce/scopes/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -12693,228 +18389,13 @@ } } ] - } - }, - "/api/geenforce/scopes/{scopeid}/preview": { - "get": { - "tags": [ - "plugin-geenforce" - ], - "summary": "Render the DRAFT manifest JSON that a publish would freeze, for admin review before shipping.", - "description": "Render the DRAFT manifest JSON that a publish would freeze, for admin review before shipping.\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** path: scopeid\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/scopes/3/preview\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "scopeid", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - } - }, - "/api/geenforce/applications": { - "get": { - "tags": [ - "plugin-geenforce" - ], - "summary": "List the core active Applications catalog (appid + appname) for the curated entry-to-app link picker in the entry...", - "description": "List the core active Applications catalog (appid + appname) for the curated entry-to-app link picker in the entry editor.\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/applications\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/geenforce/scopes/{scopeid}/entries": { - "post": { - "tags": [ - "plugin-geenforce" - ], - "summary": "Create a manifest entry in a scope at the next sortorder; validates Name (required) and Type (must be in ENTRY_TYPES)...", - "description": "Create a manifest entry in a scope at the next sortorder; validates Name (required) and Type (must be in ENTRY_TYPES); 400 on duplicate Name in scope; returns 201 with entry payload.\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** path: scopeid; JSON body: Name (required), Type (required, one of ENTRY_TYPES), optional appid (curated app link, unknown/non-numeric ids ignored), plus manifest fields consumed by build_entry (PCTypes, TargetHostnames, DetectionValue, etc.)\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"Name\":\"7-Zip\",\"Type\":\"App\",\"appid\":12}' http://localhost:5001/api/geenforce/scopes/3/entries\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "scopeid", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: scopeid; JSON body: Name (required), Type (required, one of ENTRY_TYPES), optional appid (curated app link, unknown/non-numeric ids ignored), plus manifest fields consumed by build_entry (PCTypes, TargetHostnames, DetectionValue, etc.)" - } - } - } - } - } - }, - "/api/geenforce/entries/{entryid}": { - "put": { - "tags": [ - "plugin-geenforce" - ], - "summary": "Replace an entry's fields from the payload (re-populates via populate_entry, re-creating the one-to-one InUseCheck)...", - "description": "Replace an entry's fields from the payload (re-populates via populate_entry, re-creating the one-to-one InUseCheck); same Name/Type validation and duplicate-Name 400 as create.\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** path: entryid; JSON body: Name (required), Type (required), optional appid, plus manifest fields\n\n**Example:**\n```\ncurl -X PUT -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"Name\":\"7-Zip\",\"Type\":\"App\",\"DetectionValue\":\"24.08\"}' http://localhost:5001/api/geenforce/entries/17\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "entryid", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: entryid; JSON body: Name (required), Type (required), optional appid, plus manifest fields" - } - } - } - } }, - "delete": { + "get": { "tags": [ "plugin-geenforce" ], - "summary": "Delete a manifest entry; returns {deleted: entryid}.", - "description": "Delete a manifest entry; returns {deleted: entryid}.\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** path: entryid\n\n**Example:**\n```\ncurl -X DELETE -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/entries/17\n```", + "summary": "UNDOCUMENTED: geenforce.get_scope has no docstring.", + "description": "UNDOCUMENTED: geenforce.get_scope has no docstring.\n\n**Auth:** permission:geenforce.view or geenforce.manage\n\n**Params:** path: scopeid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/geenforce/scopes/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -12949,7 +18430,7 @@ }, "parameters": [ { - "name": "entryid", + "name": "scopeid", "in": "path", "required": true, "schema": { @@ -12957,15 +18438,13 @@ } } ] - } - }, - "/api/geenforce/scopes/{scopeid}/entries/reorder": { + }, "put": { "tags": [ "plugin-geenforce" ], - "summary": "Set entry ordering from an entryid list; 400 unless the list is exactly the set of this scope's entry ids.", - "description": "Set entry ordering from an entryid list; 400 unless the list is exactly the set of this scope's entry ids.\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** path: scopeid; JSON body: order = [entryid, ...] (must match the scope's entry ids exactly)\n\n**Example:**\n```\ncurl -X PUT -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"order\":[17,15,16]}' http://localhost:5001/api/geenforce/scopes/3/entries/reorder\n```", + "summary": "UNDOCUMENTED: geenforce.update_scope has no docstring.", + "description": "UNDOCUMENTED: geenforce.update_scope has no docstring.\n\n**Auth:** permission:geenforce.manage\n\n**Params:** path: scopeid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/geenforce/scopes/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -13015,71 +18494,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "path: scopeid; JSON body: order = [entryid, ...] (must match the scope's entry ids exactly)" + "description": "path: scopeid (int); body: JSON (fields not yet documented)" } } } } } }, - "/api/geenforce/scopes/{scopeid}/simulate": { - "get": { - "tags": [ - "plugin-geenforce" - ], - "summary": "Simulate which draft entries would apply to a given machine profile and why the rest are filtered out...", - "description": "Simulate which draft entries would apply to a given machine profile and why the rest are filtered out (PCTypes/TargetHostnames/TargetMachineNumbers/_CmmVersion), using the engine-mirror filters.\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** path: scopeid; query (all optional): pctype (defaults to scopename), subtype, hostname, machinenumber, cmmversion; phase comes from the scope\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $JWT\" 'http://localhost:5001/api/geenforce/scopes/3/simulate?hostname=WKSTN0042&cmmversion=2023.2'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "scopeid", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - } - }, "/api/geenforce/scopes/{scopeid}/compliance": { "get": { "tags": [ "plugin-geenforce" ], - "summary": "Fleet install coverage per app-linked entry (installed/version-match counts from the computers plugin's...", - "description": "Fleet install coverage per app-linked entry (installed/version-match counts from the computers plugin's ComputerInstalledApp; null counts with computersplugin:false when that plugin is absent).\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** path: scopeid\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/scopes/3/compliance\n```", + "summary": "Fleet-install coverage per app-linked entry (from collected PC data).", + "description": "Fleet-install coverage per app-linked entry (from collected PC data).\n\n**Auth:** permission:geenforce.view or geenforce.manage\n\n**Params:** path: scopeid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/geenforce/scopes/1/compliance -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -13124,13 +18552,13 @@ ] } }, - "/api/geenforce/entries/{entryid}/payload": { + "/api/geenforce/scopes/{scopeid}/entries": { "post": { "tags": [ "plugin-geenforce" ], - "summary": "Upload an inline payload file (max 1 MB, rejects empty) for an entry and point the entry at it (stores sha256...", - "description": "Upload an inline payload file (max 1 MB, rejects empty) for an entry and point the entry at it (stores sha256, filename, mimetype); returns 201 with entry payload.\n\n**Auth:** jwt + permission:geenforce.publish\n\n**Params:** path: entryid; multipart/form-data: file (required)\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $JWT\" -F 'file=@fix.ps1' http://localhost:5001/api/geenforce/entries/17/payload\n```", + "summary": "UNDOCUMENTED: geenforce.create_entry has no docstring.", + "description": "UNDOCUMENTED: geenforce.create_entry has no docstring.\n\n**Auth:** permission:geenforce.manage\n\n**Params:** path: scopeid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/geenforce/scopes/1/entries -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -13165,7 +18593,7 @@ }, "parameters": [ { - "name": "entryid", + "name": "scopeid", "in": "path", "required": true, "schema": { @@ -13180,18 +18608,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "path: entryid; multipart/form-data: file (required)" + "description": "path: scopeid (int); body: JSON (fields not yet documented)" } } } } - }, - "get": { + } + }, + "/api/geenforce/scopes/{scopeid}/entries/reorder": { + "put": { "tags": [ "plugin-geenforce" ], - "summary": "Download the stored inline payload bytes for an entry as an attachment (404 if the entry has no payload).", - "description": "Download the stored inline payload bytes for an entry as an attachment (404 if the entry has no payload).\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** path: entryid\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $JWT\" -o fix.ps1 http://localhost:5001/api/geenforce/entries/17/payload\n```", + "summary": "Set entry order from a list of entryids (the ordering contract).", + "description": "Set entry order from a list of entryids (the ordering contract).\n\n**Auth:** permission:geenforce.manage\n\n**Params:** path: scopeid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/geenforce/scopes/1/entries/reorder -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -13226,7 +18656,133 @@ }, "parameters": [ { - "name": "entryid", + "name": "scopeid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: scopeid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/geenforce/scopes/{scopeid}/export-share": { + "post": { + "tags": [ + "plugin-geenforce" + ], + "summary": "Write the scope's current published JSON to the configured share root (backing up the old file to _meta/history first).", + "description": "Write the scope's current published JSON to the configured share root (backing up the old file to _meta/history first).\n\n**Auth:** permission:geenforce.publish\n\n**Params:** path: scopeid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/geenforce/scopes/1/export-share -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "scopeid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: scopeid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/geenforce/scopes/{scopeid}/preview": { + "get": { + "tags": [ + "plugin-geenforce" + ], + "summary": "Render the DRAFT manifest JSON a publish would freeze (review before ship).", + "description": "Render the DRAFT manifest JSON a publish would freeze (review before ship).\n\n**Auth:** permission:geenforce.view or geenforce.manage\n\n**Params:** path: scopeid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/geenforce/scopes/1/preview -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "scopeid", "in": "path", "required": true, "schema": { @@ -13241,8 +18797,8 @@ "tags": [ "plugin-geenforce" ], - "summary": "Freeze the scope's draft into a new published version (service.publish_scope), recording the publishing user from the...", - "description": "Freeze the scope's draft into a new published version (service.publish_scope), recording the publishing user from the JWT identity and optional notes; returns 201 with the new versionnumber.\n\n**Auth:** jwt + permission:geenforce.publish\n\n**Params:** path: scopeid; JSON body (optional): notes\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"notes\":\"add 7zip 24.08\"}' http://localhost:5001/api/geenforce/scopes/3/publish\n```", + "summary": "UNDOCUMENTED: geenforce.publish_scope_route has no docstring.", + "description": "UNDOCUMENTED: geenforce.publish_scope_route has no docstring.\n\n**Auth:** permission:geenforce.publish\n\n**Params:** path: scopeid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/geenforce/scopes/1/publish -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -13292,20 +18848,185 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "path: scopeid; JSON body (optional): notes" + "description": "path: scopeid (int); body: JSON (fields not yet documented)" } } } } } }, + "/api/geenforce/scopes/{scopeid}/publish-preflight": { + "get": { + "tags": [ + "plugin-geenforce" + ], + "summary": "Would publishing this scope outrun the fleet's enforcer lib?", + "description": "Would publishing this scope outrun the fleet's enforcer lib?\n\n**Auth:** permission:geenforce.view or geenforce.manage\n\n**Params:** path: scopeid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/geenforce/scopes/1/publish-preflight -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "scopeid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/geenforce/scopes/{scopeid}/rollback": { + "post": { + "tags": [ + "plugin-geenforce" + ], + "summary": "UNDOCUMENTED: geenforce.rollback_scope_route has no docstring.", + "description": "UNDOCUMENTED: geenforce.rollback_scope_route has no docstring.\n\n**Auth:** permission:geenforce.publish\n\n**Params:** path: scopeid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/geenforce/scopes/1/rollback -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "scopeid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: scopeid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/geenforce/scopes/{scopeid}/simulate": { + "get": { + "tags": [ + "plugin-geenforce" + ], + "summary": "Which draft entries apply to a given machine profile, and why the rest are filtered out (reuses the engine-mirror...", + "description": "Which draft entries apply to a given machine profile, and why the rest are filtered out (reuses the engine-mirror filters).\n\n**Auth:** permission:geenforce.view or geenforce.manage\n\n**Params:** path: scopeid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/geenforce/scopes/1/simulate -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "scopeid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, "/api/geenforce/scopes/{scopeid}/versions": { "get": { "tags": [ "plugin-geenforce" ], - "summary": "List published versions for a scope, newest first (versionnumber, iscurrent, publishedat, publishedby, notes).", - "description": "List published versions for a scope, newest first (versionnumber, iscurrent, publishedat, publishedby, notes).\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** path: scopeid\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/scopes/3/versions\n```", + "summary": "UNDOCUMENTED: geenforce.list_versions has no docstring.", + "description": "UNDOCUMENTED: geenforce.list_versions has no docstring.\n\n**Auth:** permission:geenforce.view or geenforce.manage\n\n**Params:** path: scopeid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/geenforce/scopes/1/versions -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -13355,8 +19076,8 @@ "tags": [ "plugin-geenforce" ], - "summary": "Fetch one published version's frozen manifest JSON (parsed and returned in the success envelope).", - "description": "Fetch one published version's frozen manifest JSON (parsed and returned in the success envelope).\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** path: scopeid, versionnumber\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/scopes/3/versions/4\n```", + "summary": "UNDOCUMENTED: geenforce.get_version has no docstring.", + "description": "UNDOCUMENTED: geenforce.get_version has no docstring.\n\n**Auth:** permission:geenforce.view or geenforce.manage\n\n**Params:** path: scopeid (int), versionnumber (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/geenforce/scopes/1/versions/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -13409,139 +19130,13 @@ ] } }, - "/api/geenforce/scopes/{scopeid}/rollback": { - "post": { - "tags": [ - "plugin-geenforce" - ], - "summary": "Roll the scope's current published pointer back to an earlier versionnumber (service.rollback_scope); 400 with the...", - "description": "Roll the scope's current published pointer back to an earlier versionnumber (service.rollback_scope); 400 with the error message if the version is invalid.\n\n**Auth:** jwt + permission:geenforce.publish\n\n**Params:** path: scopeid; JSON body: versionnumber (required, int)\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"versionnumber\":3}' http://localhost:5001/api/geenforce/scopes/3/rollback\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "scopeid", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: scopeid; JSON body: versionnumber (required, int)" - } - } - } - } - } - }, - "/api/geenforce/scopes/{scopeid}/export-share": { - "post": { - "tags": [ - "plugin-geenforce" - ], - "summary": "Write the scope's current published JSON to the configured share root (geenforce_share_root setting), backing up the...", - "description": "Write the scope's current published JSON to the configured share root (geenforce_share_root setting), backing up the old file to _meta/history; 400 if the share root is unconfigured or the export fails.\n\n**Auth:** jwt + permission:geenforce.publish\n\n**Params:** path: scopeid; no body\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/scopes/3/export-share\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "scopeid", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: scopeid; no body" - } - } - } - } - } - }, - "/api/geenforce/config": { + "/api/guides": { "get": { "tags": [ - "plugin-geenforce" + "plugin-guides" ], - "summary": "Read plugin config: the on-share export root (geenforce_share_root) and the client IP allowlist CIDRs...", - "description": "Read plugin config: the on-share export root (geenforce_share_root) and the client IP allowlist CIDRs (geenforce_allowed_cidrs).\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/config\n```", + "summary": "Published guides, plus the caller's own drafts if they can author.", + "description": "Published guides, plus the caller's own drafts if they can author.\n\n**Auth:** permission:guides.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/guides -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -13572,12 +19167,12 @@ } } }, - "put": { + "post": { "tags": [ - "plugin-geenforce" + "plugin-guides" ], - "summary": "Update plugin config settings; allowedcidrs is validated/normalized (comma/newline-separated CIDRs or bare IPs, 400...", - "description": "Update plugin config settings; allowedcidrs is validated/normalized (comma/newline-separated CIDRs or bare IPs, 400 listing any bad entries) and only keys present in the body are written.\n\n**Auth:** jwt + permission:geenforce.publish\n\n**Params:** JSON body (both optional): shareroot (string path), allowedcidrs (CSV/newline CIDR list)\n\n**Example:**\n```\ncurl -X PUT -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"allowedcidrs\":\"10.20.30.0/24, 192.168.5.7\"}' http://localhost:5001/api/geenforce/config\n```", + "summary": "UNDOCUMENTED: guides.create_guide has no docstring.", + "description": "UNDOCUMENTED: guides.create_guide has no docstring.\n\n**Auth:** permission:guides.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/guides -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -13614,58 +19209,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "JSON body (both optional): shareroot (string path), allowedcidrs (CSV/newline CIDR list)" + "description": "body: JSON (fields not yet documented)" } } } } } }, - "/api/geenforce/reports": { - "get": { + "/api/guides/{guideid}": { + "delete": { "tags": [ - "plugin-geenforce" + "plugin-guides" ], - "summary": "Latest enforcement report per PC (iscurrent rows) for the fleet compliance view: applied vs latest published version...", - "description": "Latest enforcement report per PC (iscurrent rows) for the fleet compliance view: applied vs latest published version (receivedlatest flag), install/skip/fail/filtered counts, status, check-in times.\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** query (optional): hostname (ILIKE match), scopename (exact)\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $JWT\" 'http://localhost:5001/api/geenforce/reports?scopename=cmm'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/geenforce/reports/{reportid}": { - "get": { - "tags": [ - "plugin-geenforce" - ], - "summary": "One enforcement report in detail with per-entry outcomes (entryname, action, selfhealed, exitcode, message) plus...", - "description": "One enforcement report in detail with per-entry outcomes (entryname, action, selfhealed, exitcode, message) plus applied-vs-latest version comparison.\n\n**Auth:** jwt + permission:geenforce.manage\n\n**Params:** path: reportid\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/reports/42\n```", + "summary": "UNDOCUMENTED: guides.delete_guide has no docstring.", + "description": "UNDOCUMENTED: guides.delete_guide has no docstring.\n\n**Auth:** permission:guides.delete\n\n**Params:** path: guideid (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/guides/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -13700,7 +19257,253 @@ }, "parameters": [ { - "name": "reportid", + "name": "guideid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-guides" + ], + "summary": "UNDOCUMENTED: guides.update_guide has no docstring.", + "description": "UNDOCUMENTED: guides.update_guide has no docstring.\n\n**Auth:** permission:guides.view\n\n**Params:** path: guideid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/guides/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "guideid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: guideid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/guides/{guideid}/publish": { + "post": { + "tags": [ + "plugin-guides" + ], + "summary": "UNDOCUMENTED: guides.publish_guide has no docstring.", + "description": "UNDOCUMENTED: guides.publish_guide has no docstring.\n\n**Auth:** permission:guides.publish\n\n**Params:** path: guideid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/guides/1/publish -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "guideid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: guideid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/guides/{guideid}/restore/{revisionid}": { + "post": { + "tags": [ + "plugin-guides" + ], + "summary": "UNDOCUMENTED: guides.restore_revision has no docstring.", + "description": "UNDOCUMENTED: guides.restore_revision has no docstring.\n\n**Auth:** permission:guides.view\n\n**Params:** path: guideid (int), revisionid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/guides/1/restore/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "guideid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "revisionid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: guideid (int), revisionid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/guides/{guideid}/revisions": { + "get": { + "tags": [ + "plugin-guides" + ], + "summary": "UNDOCUMENTED: guides.list_revisions has no docstring.", + "description": "UNDOCUMENTED: guides.list_revisions has no docstring.\n\n**Auth:** permission:guides.view\n\n**Params:** path: guideid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/guides/1/revisions -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "guideid", "in": "path", "required": true, "schema": { @@ -13710,51 +19513,13 @@ ] } }, - "/api/geenforce/dashboard/failures": { - "get": { + "/api/guides/{guideid}/unpublish": { + "post": { "tags": [ - "plugin-geenforce" + "plugin-guides" ], - "summary": "Dashboard card: entries that FAILED on their PC's most recent enforcement cycle, host and entry named", - "description": "Dashboard card: entries that FAILED on their PC's most recent enforcement cycle, host and entry named\n\n**Auth:** permission:geenforce.manage\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/geenforce/dashboard/failures\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/geenforce/scopes/{scopeid}/publish-preflight": { - "get": { - "tags": [ - "plugin-geenforce" - ], - "summary": "Would publishing this scope outrun the fleet's enforcer library? Returns the required version, the lowest version...", - "description": "Would publishing this scope outrun the fleet's enforcer library? Returns the required version, the lowest version reporting, the hosts behind it, and whether publish would be refused. The publish itself enforces the same gate; this is how the UI warns first\n\n**Auth:** permission:geenforce.manage\n\n**Params:** scopeid in path\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/geenforce/scopes/3/publish-preflight\n```", + "summary": "UNDOCUMENTED: guides.unpublish_guide has no docstring.", + "description": "UNDOCUMENTED: guides.unpublish_guide has no docstring.\n\n**Auth:** permission:guides.publish\n\n**Params:** path: guideid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/guides/1/unpublish -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -13789,7 +19554,121 @@ }, "parameters": [ { - "name": "scopeid", + "name": "guideid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: guideid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/guides/{guideid}/working": { + "get": { + "tags": [ + "plugin-guides" + ], + "summary": "The working copy, for the editor.", + "description": "The working copy, for the editor.\n\n**Auth:** permission:guides.view\n\n**Params:** path: guideid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/guides/1/working -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "guideid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/guides/{slug}": { + "get": { + "tags": [ + "plugin-guides" + ], + "summary": "What a reader gets: the published revision.", + "description": "What a reader gets: the published revision.\n\n**Auth:** permission:guides.view\n\n**Params:** path: slug (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/guides/x -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "slug", "in": "path", "required": true, "schema": { @@ -13799,39 +19678,13 @@ ] } }, - "/api/notifications/types": { - "get": { - "tags": [ - "plugin-notifications" - ], - "summary": "List notification types, paginated, active-only by default", - "description": "List notification types, paginated, active-only by default\n\n**Auth:** none\n\n**Params:** page, per_page; active=false to include inactive types\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/notifications/types?active=false&page=1&per_page=50'\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, + "/api/guides/images": { "post": { "tags": [ - "plugin-notifications" + "plugin-guides" ], - "summary": "Create a notification type incl. expiry rule and shopfloor display config", - "description": "Create a notification type incl. expiry rule and shopfloor display config\n\n**Auth:** jwt + permission:notifications.create\n\n**Params:** body: typename (required, unique), typedescription/description, typecolor/color, expirymode (none|duration|dailytime), expirydays, expiryhour, expiryminute, splitperemployee, showemployeephoto, displaystyle (standard|carousel|grid|banner)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/notifications/types -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"typename\":\"Recognition\",\"typecolor\":\"recognition\",\"expirymode\":\"dailytime\",\"expiryhour\":8,\"splitperemployee\":true,\"showemployeephoto\":true}'\n```", + "summary": "Take a pasted or dropped image.", + "description": "Take a pasted or dropped image.\n\n**Auth:** permission:guides.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/guides/images -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -13868,20 +19721,1066 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: typename (required, unique), typedescription/description, typecolor/color, expirymode (none|duration|dailytime), expirydays, expiryhour, expiryminute, splitperemployee, showemployeephoto, displaystyle (standard|carousel|grid|banner)" + "description": "body: JSON (fields not yet documented)" } } } } } }, - "/api/notifications/types/{type_id}": { + "/api/guides/images/{imageid}": { + "get": { + "tags": [ + "plugin-guides" + ], + "summary": "The image itself, under the same rule as the guide that holds it.", + "description": "The image itself, under the same rule as the guide that holds it.\n\n**Auth:** permission:guides.view\n\n**Params:** path: imageid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/guides/images/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "imageid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/guides/revisions/{revisionid}": { + "get": { + "tags": [ + "plugin-guides" + ], + "summary": "One revision by id.", + "description": "One revision by id.\n\n**Auth:** permission:guides.view\n\n**Params:** path: revisionid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/guides/revisions/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "revisionid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/guides/tags": { + "get": { + "tags": [ + "plugin-guides" + ], + "summary": "Every tag in use, with a count, for the filter chips.", + "description": "Every tag in use, with a count, for the filter chips.\n\n**Auth:** permission:guides.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/guides/tags -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/knowledgebase": { + "get": { + "tags": [ + "plugin-knowledgebase" + ], + "summary": "List all knowledge base articles.", + "description": "List all knowledge base articles.\n\n**Auth:** permission:kb.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/knowledgebase -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-knowledgebase" + ], + "summary": "Create a new knowledge base article.", + "description": "Create a new knowledge base article.\n\n**Auth:** permission:kb.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/knowledgebase -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/knowledgebase/{link_id}": { + "delete": { + "tags": [ + "plugin-knowledgebase" + ], + "summary": "Delete (deactivate) a knowledge base article.", + "description": "Delete (deactivate) a knowledge base article.\n\n**Auth:** permission:kb.delete\n\n**Params:** path: link_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/knowledgebase/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "link_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-knowledgebase" + ], + "summary": "Get a single knowledge base article.", + "description": "Get a single knowledge base article.\n\n**Auth:** permission:kb.view\n\n**Params:** path: link_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/knowledgebase/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "link_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, "put": { "tags": [ - "plugin-notifications" + "plugin-knowledgebase" ], - "summary": "Update a notification type (name/desc/color/isactive plus expiry and display fields)", - "description": "Update a notification type (name/desc/color/isactive plus expiry and display fields)\n\n**Auth:** jwt + permission:notifications.create\n\n**Params:** body: any of typename (unique-checked), typedescription/description, typecolor/color, isactive, expirymode, expirydays, expiryhour, expiryminute, splitperemployee, showemployeephoto, displaystyle\n\n**Example:**\n```\ncurl -X PATCH http://localhost:5001/api/notifications/types/3 -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"expirymode\":\"duration\",\"expirydays\":14}'\n```", + "summary": "Update a knowledge base article.", + "description": "Update a knowledge base article.\n\n**Auth:** permission:kb.edit\n\n**Params:** path: link_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/knowledgebase/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "link_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: link_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/knowledgebase/{link_id}/click": { + "post": { + "tags": [ + "plugin-knowledgebase" + ], + "summary": "Increment the click counter for an article the caller is allowed to read.", + "description": "Increment the click counter for an article the caller is allowed to read.\n\n**Auth:** permission:kb.view\n\n**Params:** path: link_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/knowledgebase/1/click -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "link_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: link_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/knowledgebase/stats": { + "get": { + "tags": [ + "plugin-knowledgebase" + ], + "summary": "Get knowledge base statistics.", + "description": "Get knowledge base statistics.\n\n**Auth:** permission:kb.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/knowledgebase/stats -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/machines": { + "get": { + "tags": [ + "plugin-machines" + ], + "summary": "List all machines with filtering and pagination.", + "description": "List all machines with filtering and pagination.\n\n**Auth:** permission:machines.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/machines -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-machines" + ], + "summary": "Create new machine (creates both Asset and Machine records).", + "description": "Create new machine (creates both Asset and Machine records).\n\n**Auth:** permission:machines.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/machines -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/machines/{machine_id}": { + "delete": { + "tags": [ + "plugin-machines" + ], + "summary": "Delete (soft delete) machine.", + "description": "Delete (soft delete) machine.\n\n**Auth:** permission:machines.delete\n\n**Params:** path: machine_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/machines/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "machine_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-machines" + ], + "summary": "Get a single machine item with full details.", + "description": "Get a single machine item with full details.\n\n**Auth:** permission:machines.view\n\n**Params:** path: machine_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/machines/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "machine_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-machines" + ], + "summary": "Update machine (both Asset and Machine records).", + "description": "Update machine (both Asset and Machine records).\n\n**Auth:** permission:machines.edit\n\n**Params:** path: machine_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/machines/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "machine_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: machine_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/machines/by-asset/{asset_id}": { + "get": { + "tags": [ + "plugin-machines" + ], + "summary": "Get machine data by asset ID.", + "description": "Get machine data by asset ID.\n\n**Auth:** permission:machines.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/machines/by-asset/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/machines/dashboard/outofservice": { + "get": { + "tags": [ + "plugin-machines" + ], + "summary": "Machines whose status says they are not running.", + "description": "Machines whose status says they are not running.\n\n**Auth:** permission:machines.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/machines/dashboard/outofservice -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/machines/dashboard/summary": { + "get": { + "tags": [ + "plugin-machines" + ], + "summary": "Get machine dashboard summary data.", + "description": "Get machine dashboard summary data.\n\n**Auth:** permission:machines.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/machines/dashboard/summary -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/machines/types": { + "get": { + "tags": [ + "plugin-machines" + ], + "summary": "List all machine types.", + "description": "List all machine types.\n\n**Auth:** permission:machines.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/machines/types -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-machines" + ], + "summary": "Create a new machine type.", + "description": "Create a new machine type.\n\n**Auth:** permission:machines.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/machines/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/machines/types/{type_id}": { + "delete": { + "tags": [ + "plugin-machines" + ], + "summary": "Delete a machine type. Refused if any asset still uses it.", + "description": "Delete a machine type. Refused if any asset still uses it.\n\n**Auth:** permission:machines.delete\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/machines/types/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-machines" + ], + "summary": "Get a single machine type.", + "description": "Get a single machine type.\n\n**Auth:** permission:machines.view\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/machines/types/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-machines" + ], + "summary": "Update a machine type.", + "description": "Update a machine type.\n\n**Auth:** permission:machines.edit\n\n**Params:** path: type_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/machines/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -13931,18 +20830,2323 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: any of typename (unique-checked), typedescription/description, typecolor/color, isactive, expirymode, expirydays, expiryhour, expiryminute, splitperemployee, showemployeephoto, displaystyle" + "description": "path: type_id (int); body: JSON (fields not yet documented)" } } } } + } + }, + "/api/measuringtools": { + "get": { + "tags": [ + "plugin-measuringtools" + ], + "summary": "List measuring tools with filters + pagination.", + "description": "List measuring tools with filters + pagination.\n\n**Auth:** permission:measuringtools.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/measuringtools -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } }, + "post": { + "tags": [ + "plugin-measuringtools" + ], + "summary": "Create a measuring tool: one Asset core row + one extension row.", + "description": "Create a measuring tool: one Asset core row + one extension row.\n\n**Auth:** permission:measuringtools.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/measuringtools -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/measuringtools/{tool_id}": { + "delete": { + "tags": [ + "plugin-measuringtools" + ], + "summary": "Soft delete a measuring tool (deactivates its asset).", + "description": "Soft delete a measuring tool (deactivates its asset).\n\n**Auth:** permission:measuringtools.delete\n\n**Params:** path: tool_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/measuringtools/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "tool_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-measuringtools" + ], + "summary": "Get one measuring tool (asset core merged with the extension).", + "description": "Get one measuring tool (asset core merged with the extension).\n\n**Auth:** permission:measuringtools.view\n\n**Params:** path: tool_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/measuringtools/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "tool_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-measuringtools" + ], + "summary": "Update a measuring tool (asset core fields + extension in one payload).", + "description": "Update a measuring tool (asset core fields + extension in one payload).\n\n**Auth:** permission:measuringtools.edit\n\n**Params:** path: tool_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/measuringtools/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "tool_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: tool_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/measuringtools/by-asset/{asset_id}": { + "get": { + "tags": [ + "plugin-measuringtools" + ], + "summary": "Get a measuring tool by its core asset id.", + "description": "Get a measuring tool by its core asset id.\n\n**Auth:** permission:measuringtools.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/measuringtools/by-asset/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/measuringtools/map-overlay": { + "get": { + "tags": [ + "plugin-measuringtools" + ], + "summary": "Calibration-status overlay for active measuring tools.", + "description": "Calibration-status overlay for active measuring tools.\n\n**Auth:** permission:measuringtools.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/measuringtools/map-overlay -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/measuringtools/report/calibration": { + "get": { + "tags": [ + "plugin-measuringtools" + ], + "summary": "Counts + lists bucketed by derived calibration status.", + "description": "Counts + lists bucketed by derived calibration status.\n\n**Auth:** permission:measuringtools.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/measuringtools/report/calibration -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/measuringtools/types": { + "get": { + "tags": [ + "plugin-measuringtools" + ], + "summary": "List measuring-tool types.", + "description": "List measuring-tool types.\n\n**Auth:** permission:measuringtools.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/measuringtools/types -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-measuringtools" + ], + "summary": "Create a measuring-tool type (reactivates a soft-deleted same-named one).", + "description": "Create a measuring-tool type (reactivates a soft-deleted same-named one).\n\n**Auth:** permission:measuringtools.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/measuringtools/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/measuringtools/types/{type_id}": { + "delete": { + "tags": [ + "plugin-measuringtools" + ], + "summary": "Delete a measuring-tool type. Refused if any tool still uses it.", + "description": "Delete a measuring-tool type. Refused if any tool still uses it.\n\n**Auth:** permission:measuringtools.delete\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/measuringtools/types/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-measuringtools" + ], + "summary": "Get one measuring-tool type.", + "description": "Get one measuring-tool type.\n\n**Auth:** permission:measuringtools.view\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/measuringtools/types/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-measuringtools" + ], + "summary": "Update a measuring-tool type.", + "description": "Update a measuring-tool type.\n\n**Auth:** permission:measuringtools.edit\n\n**Params:** path: type_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/measuringtools/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: type_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/network": { + "get": { + "tags": [ + "plugin-network" + ], + "summary": "List all network devices with filtering and pagination.", + "description": "List all network devices with filtering and pagination.\n\n**Auth:** permission:network.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/network -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-network" + ], + "summary": "Create new network device (creates both Asset and NetworkDevice records).", + "description": "Create new network device (creates both Asset and NetworkDevice records).\n\n**Auth:** permission:network.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/network -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/network/{device_id}": { + "delete": { + "tags": [ + "plugin-network" + ], + "summary": "Delete (soft delete) network device.", + "description": "Delete (soft delete) network device.\n\n**Auth:** permission:network.delete\n\n**Params:** path: device_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/network/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "device_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-network" + ], + "summary": "Get a single network device with full details.", + "description": "Get a single network device with full details.\n\n**Auth:** permission:network.view\n\n**Params:** path: device_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/network/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "device_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-network" + ], + "summary": "Update network device (both Asset and NetworkDevice records).", + "description": "Update network device (both Asset and NetworkDevice records).\n\n**Auth:** permission:network.edit\n\n**Params:** path: device_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/network/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "device_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: device_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/network/by-asset/{asset_id}": { + "get": { + "tags": [ + "plugin-network" + ], + "summary": "Get network device data by asset ID.", + "description": "Get network device data by asset ID.\n\n**Auth:** permission:network.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/network/by-asset/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/network/by-hostname/{hostname}": { + "get": { + "tags": [ + "plugin-network" + ], + "summary": "Get network device by hostname.", + "description": "Get network device by hostname.\n\n**Auth:** permission:network.view\n\n**Params:** path: hostname (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/network/by-hostname/x -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "hostname", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ] + } + }, + "/api/network/dashboard/summary": { + "get": { + "tags": [ + "plugin-network" + ], + "summary": "Get network device dashboard summary data.", + "description": "Get network device dashboard summary data.\n\n**Auth:** permission:network.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/network/dashboard/summary -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/network/subnets": { + "get": { + "tags": [ + "plugin-network" + ], + "summary": "List all subnets with filtering and pagination.", + "description": "List all subnets with filtering and pagination.\n\n**Auth:** permission:network.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/network/subnets -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-network" + ], + "summary": "Create a new subnet.", + "description": "Create a new subnet.\n\n**Auth:** permission:network.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/network/subnets -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/network/subnets/{subnet_id}": { + "delete": { + "tags": [ + "plugin-network" + ], + "summary": "Delete (soft delete) a subnet.", + "description": "Delete (soft delete) a subnet.\n\n**Auth:** permission:network.delete\n\n**Params:** path: subnet_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/network/subnets/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "subnet_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-network" + ], + "summary": "Get a single subnet plus the network devices whose primary IP falls in its CIDR range.", + "description": "Get a single subnet plus the network devices whose primary IP falls in its CIDR range.\n\n**Auth:** permission:network.view\n\n**Params:** path: subnet_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/network/subnets/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "subnet_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-network" + ], + "summary": "Update a subnet.", + "description": "Update a subnet.\n\n**Auth:** permission:network.edit\n\n**Params:** path: subnet_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/network/subnets/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "subnet_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: subnet_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/network/types": { + "get": { + "tags": [ + "plugin-network" + ], + "summary": "List all network device types.", + "description": "List all network device types.\n\n**Auth:** permission:network.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/network/types -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-network" + ], + "summary": "Create a new network device type.", + "description": "Create a new network device type.\n\n**Auth:** permission:network.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/network/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/network/types/{type_id}": { + "delete": { + "tags": [ + "plugin-network" + ], + "summary": "Delete a network device type. Refused if any device still uses it.", + "description": "Delete a network device type. Refused if any device still uses it.\n\n**Auth:** permission:network.delete\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/network/types/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-network" + ], + "summary": "Get a single network device type.", + "description": "Get a single network device type.\n\n**Auth:** permission:network.view\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/network/types/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-network" + ], + "summary": "Update a network device type.", + "description": "Update a network device type.\n\n**Auth:** permission:network.edit\n\n**Params:** path: type_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/network/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: type_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/network/vlans": { + "get": { + "tags": [ + "plugin-network" + ], + "summary": "List all VLANs with filtering and pagination.", + "description": "List all VLANs with filtering and pagination.\n\n**Auth:** permission:network.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/network/vlans -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-network" + ], + "summary": "Create a new VLAN.", + "description": "Create a new VLAN.\n\n**Auth:** permission:network.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/network/vlans -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/network/vlans/{vlan_id}": { + "delete": { + "tags": [ + "plugin-network" + ], + "summary": "Delete (soft delete) a VLAN.", + "description": "Delete (soft delete) a VLAN.\n\n**Auth:** permission:network.delete\n\n**Params:** path: vlan_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/network/vlans/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "vlan_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-network" + ], + "summary": "Get a single VLAN with its subnets.", + "description": "Get a single VLAN with its subnets.\n\n**Auth:** permission:network.view\n\n**Params:** path: vlan_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/network/vlans/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "vlan_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-network" + ], + "summary": "Update a VLAN.", + "description": "Update a VLAN.\n\n**Auth:** permission:network.edit\n\n**Params:** path: vlan_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/network/vlans/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "vlan_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: vlan_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/notifications": { + "get": { + "tags": [ + "plugin-notifications" + ], + "summary": "List all notifications with filtering and pagination.", + "description": "List all notifications with filtering and pagination.\n\n**Auth:** permission:notifications.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/notifications -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-notifications" + ], + "summary": "Create a new notification.", + "description": "Create a new notification.\n\n**Auth:** permission:notifications.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/notifications -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/notifications/{notification_id}": { + "delete": { + "tags": [ + "plugin-notifications" + ], + "summary": "Delete (soft delete) a notification.", + "description": "Delete (soft delete) a notification.\n\n**Auth:** permission:notifications.delete\n\n**Params:** path: notification_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/notifications/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "notification_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-notifications" + ], + "summary": "Get a single notification.", + "description": "Get a single notification.\n\n**Auth:** permission:notifications.view\n\n**Params:** path: notification_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/notifications/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "notification_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-notifications" + ], + "summary": "Update a notification.", + "description": "Update a notification.\n\n**Auth:** permission:notifications.edit\n\n**Params:** path: notification_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/notifications/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "notification_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: notification_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/notifications/active": { + "get": { + "tags": [ + "plugin-notifications" + ], + "summary": "Get currently active notifications for display.", + "description": "Get currently active notifications for display.\n\n**Auth:** permission:notifications.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/notifications/active -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/notifications/calendar": { + "get": { + "tags": [ + "plugin-notifications" + ], + "summary": "Get notifications in FullCalendar event format.", + "description": "Get notifications in FullCalendar event format.\n\n**Auth:** permission:notifications.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/notifications/calendar -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/notifications/dashboard/active": { + "get": { + "tags": [ + "plugin-notifications" + ], + "summary": "Notifications currently showing on the floor.", + "description": "Notifications currently showing on the floor.\n\n**Auth:** permission:notifications.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/notifications/dashboard/active -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/notifications/dashboard/summary": { + "get": { + "tags": [ + "plugin-notifications" + ], + "summary": "Get notifications dashboard summary.", + "description": "Get notifications dashboard summary.\n\n**Auth:** permission:notifications.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/notifications/dashboard/summary -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/notifications/employee/{sso}": { + "get": { + "tags": [ + "plugin-notifications" + ], + "summary": "Get recognitions for a specific employee by SSO.", + "description": "Get recognitions for a specific employee by SSO.\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** path: sso (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/notifications/employee/x\n```", + "security": [], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "sso", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ] + } + }, + "/api/notifications/shopfloor": { + "get": { + "tags": [ + "plugin-notifications" + ], + "summary": "Get notifications for shopfloor TV dashboard.", + "description": "Get notifications for shopfloor TV dashboard.\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/notifications/shopfloor\n```", + "security": [], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + } + } + }, + "/api/notifications/types": { + "get": { + "tags": [ + "plugin-notifications" + ], + "summary": "List all notification types.", + "description": "List all notification types.\n\n**Auth:** permission:notifications.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/notifications/types -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-notifications" + ], + "summary": "Create a new notification type.", + "description": "Create a new notification type.\n\n**Auth:** permission:notifications.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/notifications/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/notifications/types/{type_id}": { "patch": { "tags": [ "plugin-notifications" ], - "summary": "Update a notification type (name/desc/color/isactive plus expiry and display fields)", - "description": "Update a notification type (name/desc/color/isactive plus expiry and display fields)\n\n**Auth:** jwt + permission:notifications.create\n\n**Params:** body: any of typename (unique-checked), typedescription/description, typecolor/color, isactive, expirymode, expirydays, expiryhour, expiryminute, splitperemployee, showemployeephoto, displaystyle\n\n**Example:**\n```\ncurl -X PATCH http://localhost:5001/api/notifications/types/3 -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"expirymode\":\"duration\",\"expirydays\":14}'\n```", + "summary": "Update a notification type, including its auto-expiry rule.", + "description": "Update a notification type, including its auto-expiry rule.\n\n**Auth:** permission:notifications.create\n\n**Params:** path: type_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PATCH http://localhost:5001/api/notifications/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -13992,135 +23196,18 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: any of typename (unique-checked), typedescription/description, typecolor/color, isactive, expirymode, expirydays, expiryhour, expiryminute, splitperemployee, showemployeephoto, displaystyle" + "description": "path: type_id (int); body: JSON (fields not yet documented)" } } } } - } - }, - "/api/notifications": { - "get": { - "tags": [ - "plugin-notifications" - ], - "summary": "List notifications with filters, newest-first, paginated", - "description": "List notifications with filters, newest-first, paginated\n\n**Auth:** none\n\n**Params:** page, per_page; active=false to include inactive; typeid/type_id; ticketnumber (exact match, for idempotent import); current=true (within start/end window now); search (ILIKE on text)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/notifications?current=true&typeid=2&search=outage&page=1'\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "plugin-notifications" - ], - "summary": "Create a notification; endtime auto-derived from type expiry rule when omitted", - "description": "Create a notification; endtime auto-derived from type expiry rule when omitted\n\n**Auth:** jwt + permission:notifications.create\n\n**Params:** body: notification/message (required), notificationtypeid, businessunitid, appid, starttime/startdate (ISO, default now), endtime/enddate (ISO), ticketnumber, link/linkurl, isshopfloor (default false), employeesso (comma-list allowed), employeename\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/notifications -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"notification\":\"Line 2 press down\",\"notificationtypeid\":1,\"isshopfloor\":true,\"ticketnumber\":\"INC0012345\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: notification/message (required), notificationtypeid, businessunitid, appid, starttime/startdate (ISO, default now), endtime/enddate (ISO), ticketnumber, link/linkurl, isshopfloor (default false), employeesso (comma-list allowed), employeename" - } - } - } - } - } - }, - "/api/notifications/{notification_id}": { - "get": { - "tags": [ - "plugin-notifications" - ], - "summary": "Get a single notification by ID", - "description": "Get a single notification by ID\n\n**Auth:** none\n\n**Params:** path: notification_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/notifications/42\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "notification_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] }, "put": { "tags": [ "plugin-notifications" ], - "summary": "Update any notification field; empty starttime resets to now, empty endtime clears it", - "description": "Update any notification field; empty starttime resets to now, empty endtime clears it\n\n**Auth:** jwt + permission:notifications.edit\n\n**Params:** body: notification/message, notificationtypeid, businessunitid, appid, ticketnumber, link/linkurl, isactive, isshopfloor, employeesso, employeename, starttime/startdate, endtime/enddate\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/notifications/42 -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"endtime\":\"2026-08-01T12:00:00Z\",\"isactive\":true}'\n```", + "summary": "Update a notification type, including its auto-expiry rule.", + "description": "Update a notification type, including its auto-expiry rule.\n\n**Auth:** permission:notifications.create\n\n**Params:** path: type_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PATCH http://localhost:5001/api/notifications/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -14155,7 +23242,7 @@ }, "parameters": [ { - "name": "notification_id", + "name": "type_id", "in": "path", "required": true, "schema": { @@ -14170,18 +23257,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: notification/message, notificationtypeid, businessunitid, appid, ticketnumber, link/linkurl, isactive, isshopfloor, employeesso, employeename, starttime/startdate, endtime/enddate" + "description": "path: type_id (int); body: JSON (fields not yet documented)" } } } } - }, + } + }, + "/api/printedparts/files/{file_id}": { "delete": { "tags": [ - "plugin-notifications" + "plugin-printedparts" ], - "summary": "Soft-delete a notification (sets isactive=false, row kept)", - "description": "Soft-delete a notification (sets isactive=false, row kept)\n\n**Auth:** jwt + permission:notifications.delete\n\n**Params:** path: notification_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/notifications/42 -H \"Authorization: Bearer $TOKEN\"\n```", + "summary": "Remove a bad revision (wrong file uploaded). History otherwise stays.", + "description": "Remove a bad revision (wrong file uploaded). History otherwise stays.\n\n**Auth:** permission:printedparts.delete\n\n**Params:** path: file_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/printedparts/files/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -14216,7 +23305,7 @@ }, "parameters": [ { - "name": "notification_id", + "name": "file_id", "in": "path", "required": true, "schema": { @@ -14226,166 +23315,13 @@ ] } }, - "/api/notifications/active": { + "/api/printedparts/files/{file_id}/download": { "get": { "tags": [ - "plugin-notifications" + "plugin-printedparts" ], - "summary": "Currently active notifications for display, incl. ones starting within a 10-day lookahead", - "description": "Currently active notifications for display, incl. ones starting within a 10-day lookahead\n\n**Auth:** none\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/notifications/active\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/notifications/calendar": { - "get": { - "tags": [ - "plugin-notifications" - ], - "summary": "Active notifications as FullCalendar event objects for a date range", - "description": "Active notifications as FullCalendar event objects for a date range\n\n**Auth:** none\n\n**Params:** start (ISO), end (ISO); invalid dates silently ignored\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/notifications/calendar?start=2026-07-01T00:00:00Z&end=2026-07-31T23:59:59Z'\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/notifications/dashboard/summary": { - "get": { - "tags": [ - "plugin-notifications" - ], - "summary": "Dashboard counts: total currently-active notifications plus active counts grouped by type/color", - "description": "Dashboard counts: total currently-active notifications plus active counts grouped by type/color\n\n**Auth:** none\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/notifications/dashboard/summary\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/notifications/employee/{sso}": { - "get": { - "tags": [ - "plugin-notifications" - ], - "summary": "All active recognition-type notifications mentioning an employee SSO (exact or within comma-separated employeesso list)", - "description": "All active recognition-type notifications mentioning an employee SSO (exact or within comma-separated employeesso list)\n\n**Auth:** none\n\n**Params:** path: sso (digits only, 400 otherwise)\n\n**Example:**\n```\ncurl http://localhost:5001/api/notifications/employee/212345678\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "sso", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/notifications/shopfloor": { - "get": { - "tags": [ - "plugin-notifications" - ], - "summary": "Shopfloor TV feed: current cards (active now, or ended <30 min ago flagged resolved) + upcoming (starts within 5 days)...", - "description": "Shopfloor TV feed: current cards (active now, or ended <30 min ago flagged resolved) + upcoming (starts within 5 days); splits multi-employee cards per type config, resolves employee names/photos via employees plugin, returns configversion hash so kiosks reload on layout changes\n\n**Auth:** none\n\n**Params:** businessunit (numeric BU id: returns that BU's plus null-BU notifications; omitted: null-BU only)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/notifications/shopfloor?businessunit=3'\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/notifications/dashboard/active": { - "get": { - "tags": [ - "plugin-notifications" - ], - "summary": "Dashboard card: the notifications currently showing on the shop-floor board, so the office can see what the floor sees", - "description": "Dashboard card: the notifications currently showing on the shop-floor board, so the office can see what the floor sees\n\n**Auth:** jwt\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/notifications/dashboard/active\n```", + "summary": "Download a revision under its original filename.", + "description": "Download a revision under its original filename.\n\n**Auth:** permission:printedparts.view\n\n**Params:** path: file_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printedparts/files/1/download -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -14413,45 +23349,30 @@ }, "403": { "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/slides/feed": { - "get": { - "tags": [ - "plugin-slides" - ], - "summary": "Public flat playlist for a surface (lobby/shopfloor); raw jsonify, not the success_response envelope, so the...", - "description": "Public flat playlist for a surface (lobby/shopfloor); raw jsonify, not the success_response envelope, so the screensaver parser works unchanged\n\n**Auth:** none\n\n**Params:** query: surface=lobby|shopfloor (default lobby; invalid values fall back to lobby). Returns {success, surface, basepath, interval, slides:[{filename, seconds}]}; only slides whose file exists on disk are listed\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/slides/feed?surface=shopfloor'\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "file_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" } } - } + ] } }, - "/api/slides/img/{surface}/{filename}": { + "/api/printedparts/image/{filename}": { "get": { "tags": [ - "plugin-slides" + "plugin-printedparts" ], - "summary": "Public serving of a single slide image from instance/slides//, with basename path-traversal guard; 404 on...", - "description": "Public serving of a single slide image from instance/slides//, with basename path-traversal guard; 404 on unknown surface, traversal attempt, or missing file\n\n**Auth:** none\n\n**Params:** path: surface (lobby|shopfloor), filename (must equal its basename)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/slides/img/lobby/Slide1.png' -o Slide1.png\n```", + "summary": "Serve an uploaded item image (public - kiosk and list read it).", + "description": "Serve an uploaded item image (public - kiosk and list read it).\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** path: filename (path)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printedparts/image/example.png\n```", "security": [], "responses": { "200": { @@ -14475,14 +23396,6 @@ } }, "parameters": [ - { - "name": "surface", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, { "name": "filename", "in": "path", @@ -14495,13 +23408,2672 @@ ] } }, + "/api/printedparts/items": { + "get": { + "tags": [ + "plugin-printedparts" + ], + "summary": "List printed items, paginated; search + low-stock filter.", + "description": "List printed items, paginated; search + low-stock filter.\n\n**Auth:** permission:printedparts.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printedparts/items -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-printedparts" + ], + "summary": "Create a printed item.", + "description": "Create a printed item.\n\n**Auth:** permission:printedparts.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/printedparts/items -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printedparts/items/{item_id}": { + "delete": { + "tags": [ + "plugin-printedparts" + ], + "summary": "Soft-retire an item; its ledger history stays.", + "description": "Soft-retire an item; its ledger history stays.\n\n**Auth:** permission:printedparts.delete\n\n**Params:** path: item_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/printedparts/items/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "item_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-printedparts" + ], + "summary": "Get one printed item with its recent transactions.", + "description": "Get one printed item with its recent transactions.\n\n**Auth:** permission:printedparts.view\n\n**Params:** path: item_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printedparts/items/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "item_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-printedparts" + ], + "summary": "Update catalog fields. Quantity moves ONLY through the ledger.", + "description": "Update catalog fields. Quantity moves ONLY through the ledger.\n\n**Auth:** permission:printedparts.edit\n\n**Params:** path: item_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/printedparts/items/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "item_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: item_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printedparts/items/{item_id}/adjust": { + "post": { + "tags": [ + "plugin-printedparts" + ], + "summary": "Correct the count (damage, recount). Body: {quantitychange, reason, badge}.", + "description": "Correct the count (damage, recount). Body: {quantitychange, reason, badge}.\n\n**Auth:** permission:printedparts.restock\n\n**Params:** path: item_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/printedparts/items/1/adjust -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "item_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: item_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printedparts/items/{item_id}/files": { + "get": { + "tags": [ + "plugin-printedparts" + ], + "summary": "Revision history, newest first.", + "description": "Revision history, newest first.\n\n**Auth:** permission:printedparts.view\n\n**Params:** path: item_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printedparts/items/1/files -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "item_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "post": { + "tags": [ + "plugin-printedparts" + ], + "summary": "Upload the next revision of the item's print file.", + "description": "Upload the next revision of the item's print file.\n\n**Auth:** permission:printedparts.edit\n\n**Params:** path: item_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/printedparts/items/1/files -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "item_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: item_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printedparts/items/{item_id}/image": { + "delete": { + "tags": [ + "plugin-printedparts" + ], + "summary": "Clear an item image; delete the file only if this plugin owns it.", + "description": "Clear an item image; delete the file only if this plugin owns it.\n\n**Auth:** permission:printedparts.delete\n\n**Params:** path: item_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/printedparts/items/1/image -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "item_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "post": { + "tags": [ + "plugin-printedparts" + ], + "summary": "Upload (or replace) the photo for an item (multipart file=).", + "description": "Upload (or replace) the photo for an item (multipart file=).\n\n**Auth:** permission:printedparts.edit\n\n**Params:** path: item_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/printedparts/items/1/image -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "item_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: item_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printedparts/items/{item_id}/restock": { + "post": { + "tags": [ + "plugin-printedparts" + ], + "summary": "Add freshly printed stock. Body: {quantity, badge}.", + "description": "Add freshly printed stock. Body: {quantity, badge}.\n\n**Auth:** permission:printedparts.restock\n\n**Params:** path: item_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/printedparts/items/1/restock -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "item_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: item_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printedparts/items/{item_id}/restore": { + "post": { + "tags": [ + "plugin-printedparts" + ], + "summary": "Bring a retired item back; code, photo, and history are intact.", + "description": "Bring a retired item back; code, photo, and history are intact.\n\n**Auth:** permission:printedparts.delete\n\n**Params:** path: item_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/printedparts/items/1/restore -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "item_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: item_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printedparts/kiosk/item/{itemcode}": { + "get": { + "tags": [ + "plugin-printedparts" + ], + "summary": "Item summary for a scanned bin barcode. Kiosk-token gated.", + "description": "Item summary for a scanned bin barcode. Kiosk-token gated.\n\n**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)\n\n**Params:** path: itemcode (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printedparts/kiosk/item/x\n```", + "security": [ + { + "apiKeyAuth": [] + }, + {} + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "itemcode", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ] + } + }, + "/api/printedparts/kiosk/provisioning-link": { + "get": { + "tags": [ + "plugin-printedparts" + ], + "summary": "The URL that provisions a screen, token included, for an administrator.", + "description": "The URL that provisions a screen, token included, for an administrator.\n\n**Auth:** permission:settings.edit\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printedparts/kiosk/provisioning-link -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/printedparts/kiosk/take": { + "post": { + "tags": [ + "plugin-printedparts" + ], + "summary": "Take parts from a bin. Body: {itemcode, badge, quantity}.", + "description": "Take parts from a bin. Body: {itemcode, badge, quantity}.\n\n**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)\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/printedparts/kiosk/take -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "apiKeyAuth": [] + }, + {} + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printedparts/reports/by-person": { + "get": { + "tags": [ + "plugin-printedparts" + ], + "summary": "Takes grouped by employee, optionally bounded by ?days= (default 30).", + "description": "Takes grouped by employee, optionally bounded by ?days= (default 30).\n\n**Auth:** permission:printedparts.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printedparts/reports/by-person -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/printedparts/reports/consumption": { + "get": { + "tags": [ + "plugin-printedparts" + ], + "summary": "Takes per item, optionally bounded by ?days= (default 30).", + "description": "Takes per item, optionally bounded by ?days= (default 30).\n\n**Auth:** permission:printedparts.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printedparts/reports/consumption -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/printedparts/reports/stock": { + "get": { + "tags": [ + "plugin-printedparts" + ], + "summary": "Stock levels with low-stock flags and the cache-vs-ledger reconcile.", + "description": "Stock levels with low-stock flags and the cache-vs-ledger reconcile.\n\n**Auth:** permission:printedparts.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printedparts/reports/stock -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/printers": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "List all printers with filtering and pagination.", + "description": "List all printers with filtering and pagination.\n\n**Auth:** permission:printers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-printers" + ], + "summary": "Create new printer (creates both Asset and Printer records).", + "description": "Create new printer (creates both Asset and Printer records).\n\n**Auth:** permission:printers.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/printers -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printers/{printer_id}": { + "delete": { + "tags": [ + "plugin-printers" + ], + "summary": "Delete (soft delete) printer.", + "description": "Delete (soft delete) printer.\n\n**Auth:** permission:printers.delete\n\n**Params:** path: printer_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/printers/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "printer_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "Get a single printer with full details.", + "description": "Get a single printer with full details.\n\n**Auth:** permission:printers.view\n\n**Params:** path: printer_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "printer_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-printers" + ], + "summary": "Update printer (both Asset and Printer records).", + "description": "Update printer (both Asset and Printer records).\n\n**Auth:** permission:printers.edit\n\n**Params:** path: printer_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/printers/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "printer_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: printer_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printers/{printer_id}/supplies": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "Get supply levels from Zabbix (real-time lookup).", + "description": "Get supply levels from Zabbix (real-time lookup).\n\n**Auth:** permission:printers.view\n\n**Params:** path: printer_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/1/supplies -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "printer_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/printers/assignments/for-asset/{asset_id}": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "What THIS asset is assigned, without inheritance.", + "description": "What THIS asset is assigned, without inheritance.\n\n**Auth:** permission:printers.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/assignments/for-asset/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-printers" + ], + "summary": "Reconcile one asset's whole printer assignment in a single call.", + "description": "Reconcile one asset's whole printer assignment in a single call.\n\n**Auth:** permission:printers.edit\n\n**Params:** path: asset_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/printers/assignments/for-asset/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: asset_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printers/assignments/seed-from-observed/{asset_id}": { + "post": { + "tags": [ + "plugin-printers" + ], + "summary": "Write what a PC observed as the assignment of the asset in the path.", + "description": "Write what a PC observed as the assignment of the asset in the path.\n\n**Auth:** permission:printers.edit\n\n**Params:** path: asset_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/printers/assignments/seed-from-observed/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: asset_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printers/by-asset/{asset_id}": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "Get printer data by asset ID.", + "description": "Get printer data by asset ID.\n\n**Auth:** permission:printers.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/by-asset/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/printers/dashboard/summary": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "Get printer dashboard summary data.", + "description": "Get printer dashboard summary data.\n\n**Auth:** permission:printers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/dashboard/summary -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/printers/dashboard/supplies": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "Printers needing a cartridge, flattened to one row per printer.", + "description": "Printers needing a cartridge, flattened to one row per printer.\n\n**Auth:** permission:printers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/dashboard/supplies -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/printers/drivers": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "List printer drivers. ?active=false includes inactive ones.", + "description": "List printer drivers. ?active=false includes inactive ones.\n\n**Auth:** permission:printers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/drivers -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-printers" + ], + "summary": "UNDOCUMENTED: printers_asset.create_driver has no docstring.", + "description": "UNDOCUMENTED: printers_asset.create_driver has no docstring.\n\n**Auth:** permission:printers.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/printers/drivers -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printers/drivers/{driver_id}": { + "delete": { + "tags": [ + "plugin-printers" + ], + "summary": "UNDOCUMENTED: printers_asset.delete_driver has no docstring.", + "description": "UNDOCUMENTED: printers_asset.delete_driver has no docstring.\n\n**Auth:** permission:printers.delete\n\n**Params:** path: driver_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/printers/drivers/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "driver_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-printers" + ], + "summary": "UNDOCUMENTED: printers_asset.update_driver has no docstring.", + "description": "UNDOCUMENTED: printers_asset.update_driver has no docstring.\n\n**Auth:** permission:printers.edit\n\n**Params:** path: driver_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/printers/drivers/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "driver_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: driver_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printers/for-host/{hostname}": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "Printers assigned to a PC, by hostname, with what it takes to install one.", + "description": "Printers assigned to a PC, by hostname, with what it takes to install one.\n\n**Auth:** jwt-optional (works signed out; the handler decides what shows)\n\n**Params:** path: hostname (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/for-host/x -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + }, + {} + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "hostname", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ] + } + }, + "/api/printers/install-batch": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "Generate a Windows .bat that installs the selected printers.", + "description": "Generate a Windows .bat that installs the selected printers.\n\n**Auth:** jwt-optional (works signed out; the handler decides what shows)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/install-batch -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + }, + {} + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + } + } + }, + "/api/printers/install-list": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "Flat, unpaginated list of network printers for the printer installer.", + "description": "Flat, unpaginated list of network printers for the printer installer.\n\n**Auth:** jwt-optional (works signed out; the handler decides what shows)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/install-list -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + }, + {} + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + } + } + }, + "/api/printers/lookup": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "Find a printer by IP or FQDN. Parity with the classic printerlookup.asp.", + "description": "Find a printer by IP or FQDN. Parity with the classic printerlookup.asp.\n\n**Auth:** permission:printers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/lookup -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/printers/lowsupplies": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "Get printers with low or critical supply levels.", + "description": "Get printers with low or critical supply levels.\n\n**Auth:** permission:printers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/lowsupplies -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/printers/models": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "List models with a supply count, for the supply-management picker.", + "description": "List models with a supply count, for the supply-management picker.\n\n**Auth:** permission:printers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/models -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/printers/models/{modelnumberid}/supplies": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "List all supplies mapped to a model.", + "description": "List all supplies mapped to a model.\n\n**Auth:** permission:printers.view\n\n**Params:** path: modelnumberid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/models/1/supplies -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "modelnumberid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "post": { + "tags": [ + "plugin-printers" + ], + "summary": "Add a supply to a model.", + "description": "Add a supply to a model.\n\n**Auth:** permission:printers.create\n\n**Params:** path: modelnumberid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/printers/models/1/supplies -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "modelnumberid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: modelnumberid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printers/observed/{hostname}": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "What a host last reported, each queue judged against what it is assigned.", + "description": "What a host last reported, each queue judged against what it is assigned.\n\n**Auth:** permission:printers.view\n\n**Params:** path: hostname (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/observed/x -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "hostname", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ] + } + }, + "/api/printers/observed/for-asset/{asset_id}": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "The same comparison, reached from an asset page: one block per host.", + "description": "The same comparison, reached from an asset page: one block per host.\n\n**Auth:** permission:printers.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/observed/for-asset/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "asset_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + } + }, + "/api/printers/pc-default": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "Default printer for a PC, by machine (asset) number.", + "description": "Default printer for a PC, by machine (asset) number.\n\n**Auth:** jwt-optional (works signed out; the handler decides what shows)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/pc-default -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + }, + {} + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + } + } + }, + "/api/printers/supplies/{modelsupplyid}": { + "delete": { + "tags": [ + "plugin-printers" + ], + "summary": "Delete a model supply.", + "description": "Delete a model supply.\n\n**Auth:** permission:printers.delete\n\n**Params:** path: modelsupplyid (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/printers/supplies/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "modelsupplyid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-printers" + ], + "summary": "Update a model supply.", + "description": "Update a model supply.\n\n**Auth:** permission:printers.edit\n\n**Params:** path: modelsupplyid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/printers/supplies/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "modelsupplyid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: modelsupplyid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printers/supplies/forecast": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "Days-to-empty and replacement counts per printer.", + "description": "Days-to-empty and replacement counts per printer.\n\n**Auth:** permission:printers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/supplies/forecast -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/printers/supplies/meta": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "Allowed values for supply type, color, and capacity tier (for the UI).", + "description": "Allowed values for supply type, color, and capacity tier (for the UI).\n\n**Auth:** permission:printers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/supplies/meta -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/printers/supplies/refresh": { + "post": { + "tags": [ + "plugin-printers" + ], + "summary": "Clear cached Zabbix supply data so the next read pulls fresh values.", + "description": "Clear cached Zabbix supply data so the next read pulls fresh values.\n\n**Auth:** permission:printers.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/printers/supplies/refresh -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printers/types": { + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "List all printer types.", + "description": "List all printer types.\n\n**Auth:** permission:printers.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/types -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + }, + "post": { + "tags": [ + "plugin-printers" + ], + "summary": "Create a new printer type.", + "description": "Create a new printer type.\n\n**Auth:** permission:printers.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/printers/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/printers/types/{type_id}": { + "delete": { + "tags": [ + "plugin-printers" + ], + "summary": "Delete a printer type. Refused if any printer still uses it.", + "description": "Delete a printer type. Refused if any printer still uses it.\n\n**Auth:** permission:printers.delete\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/printers/types/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-printers" + ], + "summary": "Get a single printer type.", + "description": "Get a single printer type.\n\n**Auth:** permission:printers.view\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/types/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-printers" + ], + "summary": "Update a printer type.", + "description": "Update a printer type.\n\n**Auth:** permission:printers.edit\n\n**Params:** path: type_id (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/printers/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "type_id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: type_id (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, "/api/slides/{surface}": { "get": { "tags": [ "plugin-slides" ], - "summary": "Admin list of a surface's slides (TvSlide rows ordered by sortorder,slideid, filtered to files present on disk), each...", - "description": "Admin list of a surface's slides (TvSlide rows ordered by sortorder,slideid, filtered to files present on disk), each with a url field for the img route\n\n**Auth:** jwt + permission:slides.manage\n\n**Params:** path: surface (lobby|shopfloor; else VALIDATION_ERROR)\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/slides/lobby'\n```", + "summary": "UNDOCUMENTED: slides.list_slides has no docstring.", + "description": "UNDOCUMENTED: slides.list_slides has no docstring.\n\n**Auth:** permission:slides.view or slides.manage\n\n**Params:** path: surface (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/slides/x -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -14546,202 +26118,13 @@ ] } }, - "/api/slides/{surface}/upload": { - "post": { - "tags": [ - "plugin-slides" - ], - "summary": "Upload one or more slide images; non-image extensions skipped, names secure_filename'd and unique-renamed (stem_N.ext)...", - "description": "Upload one or more slide images; non-image extensions skipped, names secure_filename'd and unique-renamed (stem_N.ext) on collision, appended after current max sortorder in natural filename order, seconds=0 (surface default)\n\n**Auth:** jwt + permission:slides.manage\n\n**Params:** path: surface. multipart/form-data body: files (repeatable) or single file. Allowed extensions: .jpg .jpeg .png .gif .bmp .webp. Returns {added:[names]}\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -F 'files=@Slide1.png' -F 'files=@Slide2.png' 'http://localhost:5001/api/slides/lobby/upload'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "surface", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: surface. multipart/form-data body: files (repeatable) or single file. Allowed extensions: .jpg .jpeg .png .gif .bmp .webp. Returns {added:[names]}" - } - } - } - } - } - }, - "/api/slides/{surface}/order": { - "post": { - "tags": [ - "plugin-slides" - ], - "summary": "Save play order: each filename in the order array gets sortorder set to its index; unknown filenames silently ignored", - "description": "Save play order: each filename in the order array gets sortorder set to its index; unknown filenames silently ignored\n\n**Auth:** jwt + permission:slides.manage\n\n**Params:** path: surface. JSON body: {order: [filename, ...]}\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"order\":[\"Slide2.png\",\"Slide1.png\"]}' 'http://localhost:5001/api/slides/lobby/order'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "surface", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: surface. JSON body: {order: [filename, ...]}" - } - } - } - } - } - }, - "/api/slides/{surface}/delete": { - "post": { - "tags": [ - "plugin-slides" - ], - "summary": "Delete named slides: removes file from disk (OSError swallowed) and the TvSlide row; filenames reduced to basename...", - "description": "Delete named slides: removes file from disk (OSError swallowed) and the TvSlide row; filenames reduced to basename first; returns count of DB rows removed\n\n**Auth:** jwt + permission:slides.manage\n\n**Params:** path: surface. JSON body: {files: [filename, ...]}\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"files\":[\"Slide1.png\"]}' 'http://localhost:5001/api/slides/shopfloor/delete'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "surface", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: surface. JSON body: {files: [filename, ...]}" - } - } - } - } - } - }, "/api/slides/{surface}/{slideid}": { "patch": { "tags": [ "plugin-slides" ], - "summary": "Update a single slide's per-slide duration; seconds clamped to >=0 (0 means use the 10s surface default); 404 if slide...", - "description": "Update a single slide's per-slide duration; seconds clamped to >=0 (0 means use the 10s surface default); 404 if slide missing or belongs to a different surface\n\n**Auth:** jwt + permission:slides.manage\n\n**Params:** path: surface, slideid (int). JSON body: {seconds: int} (non-int -> VALIDATION_ERROR)\n\n**Example:**\n```\ncurl -X PATCH -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"seconds\":15}' 'http://localhost:5001/api/slides/lobby/12'\n```", + "summary": "UNDOCUMENTED: slides.update_slide has no docstring.", + "description": "UNDOCUMENTED: slides.update_slide has no docstring.\n\n**Auth:** permission:slides.manage\n\n**Params:** path: surface (string), slideid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PATCH http://localhost:5001/api/slides/x/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -14799,106 +26182,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "path: surface, slideid (int). JSON body: {seconds: int} (non-int -> VALIDATION_ERROR)" + "description": "path: surface (string), slideid (int); body: JSON (fields not yet documented)" } } } } } }, - "/api/printedparts/items": { - "get": { - "tags": [ - "plugin-printedparts" - ], - "summary": "List printed items, paginated, with search and low-stock filter", - "description": "List printed items, paginated, with search and low-stock filter\n\n**Auth:** jwt + permission:printedparts.view\n\n**Params:** query: page, per_page, search (matches itemcode/gagelabtag/itemname/itemdescription/binlocation), active (default true; 'false' includes retired), lowstock=true\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOK\" 'http://localhost:5001/api/printedparts/items?search=bracket&lowstock=true&page=1&per_page=25'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - }, + "/api/slides/{surface}/delete": { "post": { "tags": [ - "plugin-printedparts" + "plugin-slides" ], - "summary": "Create a printed item; itemcode auto-minted from printedparts_code_prefix setting + row id; optional gagelabtag...", - "description": "Create a printed item; itemcode auto-minted from printedparts_code_prefix setting + row id; optional gagelabtag unique-checked (409 on clash)\n\n**Auth:** jwt + permission:printedparts.create\n\n**Params:** json body: itemname (required), gagelabtag, itemdescription, lowstockthreshold (default from printedparts_default_threshold setting, fallback 5), binlocation, printnotes; quantityonhand starts at 0\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"itemname\":\"Fixture clip\",\"gagelabtag\":\"WJRP0042\",\"binlocation\":\"B3\",\"lowstockthreshold\":10}' http://localhost:5001/api/printedparts/items\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "json body: itemname (required), gagelabtag, itemdescription, lowstockthreshold (default from printedparts_default_threshold setting, fallback 5), binlocation, printnotes; quantityonhand starts at 0" - } - } - } - } - } - }, - "/api/printedparts/items/{item_id}": { - "get": { - "tags": [ - "plugin-printedparts" - ], - "summary": "Get one printed item plus its 25 most recent ledger transactions", - "description": "Get one printed item plus its 25 most recent ledger transactions\n\n**Auth:** jwt + permission:printedparts.view\n\n**Params:** path: item_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOK\" http://localhost:5001/api/printedparts/items/42\n```", + "summary": "UNDOCUMENTED: slides.delete_slides has no docstring.", + "description": "UNDOCUMENTED: slides.delete_slides has no docstring.\n\n**Auth:** permission:slides.manage\n\n**Params:** path: surface (string); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/slides/x/delete -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -14933,60 +26230,11 @@ }, "parameters": [ { - "name": "item_id", + "name": "surface", "in": "path", "required": true, "schema": { - "type": "integer" - } - } - ] - }, - "put": { - "tags": [ - "plugin-printedparts" - ], - "summary": "Update catalog fields (itemname, itemdescription, lowstockthreshold, binlocation, printnotes, gagelabtag); rejects...", - "description": "Update catalog fields (itemname, itemdescription, lowstockthreshold, binlocation, printnotes, gagelabtag); rejects quantityonhand (ledger-managed) and duplicate gagelabtag (409)\n\n**Auth:** jwt + permission:printedparts.edit\n\n**Params:** path: item_id; json body: any of the editable fields; gagelabtag uppercased, empty string clears it\n\n**Example:**\n```\ncurl -X PUT -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"binlocation\":\"C1\",\"lowstockthreshold\":8}' http://localhost:5001/api/printedparts/items/42\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "item_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" + "type": "string" } } ], @@ -14997,69 +26245,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "path: item_id; json body: any of the editable fields; gagelabtag uppercased, empty string clears it" + "description": "path: surface (string); body: JSON (fields not yet documented)" } } } } - }, - "delete": { - "tags": [ - "plugin-printedparts" - ], - "summary": "Soft-retire an item (isactive=false); ledger history preserved", - "description": "Soft-retire an item (isactive=false); ledger history preserved\n\n**Auth:** jwt + permission:printedparts.delete\n\n**Params:** path: item_id\n\n**Example:**\n```\ncurl -X DELETE -H \"Authorization: Bearer $TOK\" http://localhost:5001/api/printedparts/items/42\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "item_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] } }, - "/api/printedparts/items/{item_id}/restore": { + "/api/slides/{surface}/order": { "post": { "tags": [ - "plugin-printedparts" + "plugin-slides" ], - "summary": "Un-retire a soft-deleted item (isactive=true); code, photo, history intact", - "description": "Un-retire a soft-deleted item (isactive=true); code, photo, history intact\n\n**Auth:** jwt + permission:printedparts.delete\n\n**Params:** path: item_id\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOK\" http://localhost:5001/api/printedparts/items/42/restore\n```", + "summary": "UNDOCUMENTED: slides.reorder_slides has no docstring.", + "description": "UNDOCUMENTED: slides.reorder_slides has no docstring.\n\n**Auth:** permission:slides.manage\n\n**Params:** path: surface (string); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/slides/x/order -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -15094,11 +26293,11 @@ }, "parameters": [ { - "name": "item_id", + "name": "surface", "in": "path", "required": true, "schema": { - "type": "integer" + "type": "string" } } ], @@ -15109,20 +26308,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "path: item_id" + "description": "path: surface (string); body: JSON (fields not yet documented)" } } } } } }, - "/api/printedparts/items/{item_id}/image": { + "/api/slides/{surface}/upload": { "post": { "tags": [ - "plugin-printedparts" + "plugin-slides" ], - "summary": "Upload or replace the item's photo (png/jpg/jpeg/gif/webp); old image files for the item are deleted first, imageurl...", - "description": "Upload or replace the item's photo (png/jpg/jpeg/gif/webp); old image files for the item are deleted first, imageurl updated\n\n**Auth:** jwt + permission:printedparts.edit\n\n**Params:** path: item_id; multipart/form-data: file=\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOK\" -F 'file=@clip.jpg' http://localhost:5001/api/printedparts/items/42/image\n```", + "summary": "UNDOCUMENTED: slides.upload_slides has no docstring.", + "description": "UNDOCUMENTED: slides.upload_slides has no docstring.\n\n**Auth:** permission:slides.manage\n\n**Params:** path: surface (string); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/slides/x/upload -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -15157,11 +26356,11 @@ }, "parameters": [ { - "name": "item_id", + "name": "surface", "in": "path", "required": true, "schema": { - "type": "integer" + "type": "string" } } ], @@ -15172,23 +26371,21 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "path: item_id; multipart/form-data: file=" + "description": "path: surface (string); body: JSON (fields not yet documented)" } } } } - }, - "delete": { + } + }, + "/api/slides/feed": { + "get": { "tags": [ - "plugin-printedparts" - ], - "summary": "Clear the item's imageurl; deletes the file on disk only if the URL is plugin-owned (starts with...", - "description": "Clear the item's imageurl; deletes the file on disk only if the URL is plugin-owned (starts with /api/printedparts/image/)\n\n**Auth:** jwt + permission:printedparts.delete\n\n**Params:** path: item_id\n\n**Example:**\n```\ncurl -X DELETE -H \"Authorization: Bearer $TOK\" http://localhost:5001/api/printedparts/items/42/image\n```", - "security": [ - { - "bearerAuth": [] - } + "plugin-slides" ], + "summary": "Flat playlist for a surface. NOT wrapped in the success_response envelope so the screensaver's existing parser needs no...", + "description": "Flat playlist for a surface. NOT wrapped in the success_response envelope so the screensaver's existing parser needs no change.\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/slides/feed\n```", + "security": [], "responses": { "200": { "description": "Success. Body is the success_response envelope: {status, data, meta}.", @@ -15205,36 +26402,17 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." } - }, - "parameters": [ - { - "name": "item_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] + } } }, - "/api/printedparts/image/{filename}": { + "/api/slides/img/{surface}/{filename}": { "get": { "tags": [ - "plugin-printedparts" + "plugin-slides" ], - "summary": "Serve an uploaded item image from instance/printedpartsimages (public: fetched by tags on kiosk and lists)", - "description": "Serve an uploaded item image from instance/printedpartsimages (public: fetched by tags on kiosk and lists)\n\n**Auth:** none\n\n**Params:** path: filename (e.g. printeditem-42.jpg)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printedparts/image/printeditem-42.jpg -o clip.jpg\n```", + "summary": "Serve a slide image with a path-traversal guard.", + "description": "Serve a slide image with a path-traversal guard.\n\n**Auth:** none (reviewed: named in the public-read or mutation sweep)\n\n**Params:** path: surface (string), filename (path)\n\n**Example:**\n```\ncurl http://localhost:5001/api/slides/img/x/example.png\n```", "security": [], "responses": { "200": { @@ -15258,6 +26436,14 @@ } }, "parameters": [ + { + "name": "surface", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "filename", "in": "path", @@ -15270,17 +26456,18 @@ ] } }, - "/api/printedparts/items/{item_id}/restock": { + "/api/tools/secrets": { "post": { "tags": [ - "plugin-printedparts" + "plugin-tools" ], - "summary": "Add freshly printed stock via a 'restock' ledger write (single-commit ledger + cached quantity); badge resolved...", - "description": "Add freshly printed stock via a 'restock' ledger write (single-commit ledger + cached quantity); badge resolved server-side to sso/name; 404 if item inactive\n\n**Auth:** jwt + permission:printedparts.restock\n\n**Params:** path: item_id; json body: quantity (positive int, required), badge (required, 422 BadgeError if unresolvable)\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"quantity\":20,\"badge\":\"123456789\"}' http://localhost:5001/api/printedparts/items/42/restock\n```", + "summary": "Store an already-encrypted secret and return its token.", + "description": "Store an already-encrypted secret and return its token.\n\n**Auth:** jwt-optional (works signed out; the handler decides what shows)\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/tools/secrets -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] - } + }, + {} ], "responses": { "200": { @@ -15298,27 +26485,8 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." } }, - "parameters": [ - { - "name": "item_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], "requestBody": { "required": true, "content": { @@ -15326,83 +26494,20 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "path: item_id; json body: quantity (positive int, required), badge (required, 422 BadgeError if unresolvable)" + "description": "body: JSON (fields not yet documented)" } } } } } }, - "/api/printedparts/items/{item_id}/adjust": { - "post": { - "tags": [ - "plugin-printedparts" - ], - "summary": "Correct the count (damage, recount) via an 'adjust' ledger write; reason mandatory; rejects driving stock below zero...", - "description": "Correct the count (damage, recount) via an 'adjust' ledger write; reason mandatory; rejects driving stock below zero; fires low-stock alert on downward threshold crossing\n\n**Auth:** jwt + permission:printedparts.restock\n\n**Params:** path: item_id; json body: quantitychange (non-zero int, required), reason (required), badge (required)\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"quantitychange\":-3,\"reason\":\"damaged in bin\",\"badge\":\"123456789\"}' http://localhost:5001/api/printedparts/items/42/adjust\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "item_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: item_id; json body: quantitychange (non-zero int, required), reason (required), badge (required)" - } - } - } - } - } - }, - "/api/printedparts/kiosk/item/{itemcode}": { + "/api/tools/secrets/{token}": { "get": { "tags": [ - "plugin-printedparts" + "plugin-tools" ], - "summary": "Item summary for a scanned bin barcode; resolves full code (itemcode or gagelabtag), 'TAG|rev' QR payloads (rev...", - "description": "Item summary for a scanned bin barcode; resolves full code (itemcode or gagelabtag), 'TAG|rev' QR payloads (rev stripped), or bare keypad digits when the numeric tail uniquely matches one active item\n\n**Auth:** none (deliberately open per decision record; kiosk cannot carry JWT)\n\n**Params:** path: itemcode (e.g. WJRP0042, 3DP0042, 42, or WJRP0042|3)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printedparts/kiosk/item/WJRP0042\n```", + "summary": "Whether a link is still live, WITHOUT spending it.", + "description": "Whether a link is still live, WITHOUT spending it.\n\n**Auth:** none (the unguessable token in the path is the authorisation)\n\n**Params:** path: token (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/tools/secrets/x\n```", "security": [], "responses": { "200": { @@ -15427,7 +26532,7 @@ }, "parameters": [ { - "name": "itemcode", + "name": "token", "in": "path", "required": true, "schema": { @@ -15435,15 +26540,13 @@ } } ] - } - }, - "/api/printedparts/kiosk/take": { + }, "post": { "tags": [ - "plugin-printedparts" + "plugin-tools" ], - "summary": "Kiosk checkout: decrement-only 'take' ledger write, badge-attributed, bounded by quantityonhand; records optional...", - "description": "Kiosk checkout: decrement-only 'take' ledger write, badge-attributed, bounded by quantityonhand; records optional print-file revision (explicit field or 'TAG|rev' tail); triggers low-stock alert on threshold crossing; the product's only open write\n\n**Auth:** none (deliberately open per decision record)\n\n**Params:** json body: itemcode (required, same resolution as kiosk/item), badge (required, resolved server-side, 422 on BadgeError), quantity (positive int <= on hand), revision (optional int)\n\n**Example:**\n```\ncurl -X POST -H 'Content-Type: application/json' -d '{\"itemcode\":\"WJRP0042|3\",\"badge\":\"123456789\",\"quantity\":2}' http://localhost:5001/api/printedparts/kiosk/take\n```", + "summary": "Hand over the ciphertext once, then spend the view.", + "description": "Hand over the ciphertext once, then spend the view.\n\n**Auth:** none (the unguessable token in the path is the authorisation; redeeming spends the secret)\n\n**Params:** path: token (string); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/tools/secrets/x -H 'Content-Type: application/json' -d '{}'\n```", "security": [], "responses": { "200": { @@ -15461,155 +26564,6 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "json body: itemcode (required, same resolution as kiosk/item), badge (required, resolved server-side, 422 on BadgeError), quantity (positive int <= on hand), revision (optional int)" - } - } - } - } - } - }, - "/api/printedparts/reports/stock": { - "get": { - "tags": [ - "plugin-printedparts" - ], - "summary": "Stock-level report for active items with low-stock flags and ledgerdelta (cache-vs-ledger reconcile; nonzero means a...", - "description": "Stock-level report for active items with low-stock flags and ledgerdelta (cache-vs-ledger reconcile; nonzero means a write bypassed the single-commit rule)\n\n**Auth:** jwt-optional (like all product reports)\n\n**Params:** query: format=csv for CSV download (printedparts-stock.csv), else JSON {columns, rows}\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/printedparts/reports/stock?format=csv' -o stock.csv\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/printedparts/reports/consumption": { - "get": { - "tags": [ - "plugin-printedparts" - ], - "summary": "Take-transactions aggregated per item (takes count + quantitytaken), sorted by quantity taken descending", - "description": "Take-transactions aggregated per item (takes count + quantitytaken), sorted by quantity taken descending\n\n**Auth:** jwt-optional\n\n**Params:** query: days (default 30; 0 or negative = all time), format=csv (printedparts-consumption.csv)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/printedparts/reports/consumption?days=90'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/printedparts/reports/by-person": { - "get": { - "tags": [ - "plugin-printedparts" - ], - "summary": "Take-transactions grouped by employee SSO (takes count + quantitytaken), sorted by quantity taken descending", - "description": "Take-transactions grouped by employee SSO (takes count + quantitytaken), sorted by quantity taken descending\n\n**Auth:** jwt-optional\n\n**Params:** query: days (default 30; 0 or negative = all time), format=csv (printedparts-by-person.csv)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/printedparts/reports/by-person?days=30&format=csv' -o by-person.csv\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/printedparts/items/{item_id}/files": { - "get": { - "tags": [ - "plugin-printedparts" - ], - "summary": "List the item's print-file revision history, newest revision first", - "description": "List the item's print-file revision history, newest revision first\n\n**Auth:** jwt + permission:printedparts.view\n\n**Params:** path: item_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOK\" http://localhost:5001/api/printedparts/items/42/files\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." }, "404": { "description": "No such record." @@ -15617,946 +26571,13 @@ }, "parameters": [ { - "name": "item_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - }, - "post": { - "tags": [ - "plugin-printedparts" - ], - "summary": "Upload the next print-file revision (append-only, auto-numbered max+1); allowed ext...", - "description": "Upload the next print-file revision (append-only, auto-numbered max+1); allowed ext: stl/3mf/gcode/gco/bgcode/step/stp/obj/amf; 100 MB cap; uploader recorded from JWT identity\n\n**Auth:** jwt + permission:printedparts.edit\n\n**Params:** path: item_id; multipart/form-data: file= (required), note= (optional)\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOK\" -F 'file=@clip-v2.stl' -F 'note=thicker wall' http://localhost:5001/api/printedparts/items/42/files\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "item_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: item_id; multipart/form-data: file= (required), note= (optional)" - } - } - } - } - } - }, - "/api/printedparts/files/{file_id}/download": { - "get": { - "tags": [ - "plugin-printedparts" - ], - "summary": "Download a print-file revision as an attachment under its original filename (jwt-optional so plain anchor downloads...", - "description": "Download a print-file revision as an attachment under its original filename (jwt-optional so plain anchor downloads work)\n\n**Auth:** jwt-optional\n\n**Params:** path: file_id\n\n**Example:**\n```\ncurl -OJ http://localhost:5001/api/printedparts/files/7/download\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "file_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - } - }, - "/api/printedparts/files/{file_id}": { - "delete": { - "tags": [ - "plugin-printedparts" - ], - "summary": "Delete a bad print-file revision (wrong file uploaded): removes the stored file and the DB record", - "description": "Delete a bad print-file revision (wrong file uploaded): removes the stored file and the DB record\n\n**Auth:** jwt + permission:printedparts.delete\n\n**Params:** path: file_id\n\n**Example:**\n```\ncurl -X DELETE -H \"Authorization: Bearer $TOK\" http://localhost:5001/api/printedparts/files/7\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "file_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - } - }, - "/api/network/types": { - "get": { - "tags": [ - "plugin-network" - ], - "summary": "List network device types, paginated", - "description": "List network device types, paginated\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active (default true; 'false' includes inactive), search (ilike on networkdevicetype)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/network/types?search=switch&per_page=50'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "plugin-network" - ], - "summary": "Create a network device type (reactivates a soft-deleted duplicate instead of 409ing)", - "description": "Create a network device type (reactivates a soft-deleted duplicate instead of 409ing)\n\n**Auth:** jwt + permission:network.create\n\n**Params:** body: networkdevicetype (required), description, icon, color\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/network/types -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"networkdevicetype\":\"Switch\",\"icon\":\"mdi-switch\",\"color\":\"#2196f3\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: networkdevicetype (required), description, icon, color" - } - } - } - } - } - }, - "/api/network/types/{type_id}": { - "get": { - "tags": [ - "plugin-network" - ], - "summary": "Get one network device type by ID", - "description": "Get one network device type by ID\n\n**Auth:** jwt-optional\n\n**Params:** path: type_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/network/types/3\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - }, - "put": { - "tags": [ - "plugin-network" - ], - "summary": "Update a network device type; 409 on name collision", - "description": "Update a network device type; 409 on name collision\n\n**Auth:** jwt + permission:network.edit\n\n**Params:** body: networkdevicetype, description, icon, color, isactive\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/network/types/3 -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"description\":\"Access switches\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: networkdevicetype, description, icon, color, isactive" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-network" - ], - "summary": "Hard-delete a device type; 409 if any device still references it", - "description": "Hard-delete a device type; 409 if any device still references it\n\n**Auth:** jwt + permission:network.delete\n\n**Params:** path: type_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/network/types/3 -H \"Authorization: Bearer $TOKEN\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - } - }, - "/api/network": { - "get": { - "tags": [ - "plugin-network" - ], - "summary": "List network devices (Asset joined with NetworkDevice extension + primary IP), filtered/sorted/paginated", - "description": "List network devices (Asset joined with NetworkDevice extension + primary IP), filtered/sorted/paginated\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active, assetnumber (exact match for idempotent import), search (assetnumber/name/serialnumber/hostname/gaugelabreference/maintenancereference), typeid|type_id, vendorid|vendor_id, locationid|location_id, businessunitid|businessunit_id, poe (true/false), managed (true/false), sort (hostname|assetnumber|name), dir (asc|desc)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/network?typeid=2&poe=true&sort=hostname&dir=asc'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "plugin-network" - ], - "summary": "Create a network device (creates core Asset + NetworkDevice extension; upserts primary-IP Communication; audit-logged...", - "description": "Create a network device (creates core Asset + NetworkDevice extension; upserts primary-IP Communication; audit-logged; honors X-Import-Mode legacy timestamps)\n\n**Auth:** jwt + permission:network.create\n\n**Params:** body: assetnumber (required); name, serialnumber, gaugelabreference, maintenancereference, statusid (default 1), locationid, businessunitid, mapx, mapy, notes, networkdevicetypeid, vendorid, hostname (unique), firmwareversion, portcount, ispoe, ismanaged, rackunit, ipaddress\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/network -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"assetnumber\":\"NET-0042\",\"hostname\":\"wjf-sw-idf3-01\",\"networkdevicetypeid\":1,\"portcount\":48,\"ispoe\":true,\"ipaddress\":\"10.1.3.10\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: assetnumber (required); name, serialnumber, gaugelabreference, maintenancereference, statusid (default 1), locationid, businessunitid, mapx, mapy, notes, networkdevicetypeid, vendorid, hostname (unique), firmwareversion, portcount, ispoe, ismanaged, rackunit, ipaddress" - } - } - } - } - } - }, - "/api/network/{device_id}": { - "get": { - "tags": [ - "plugin-network" - ], - "summary": "Get one network device (asset dict + networkdevice sub-object + primary ipaddress)", - "description": "Get one network device (asset dict + networkdevice sub-object + primary ipaddress)\n\n**Auth:** jwt-optional\n\n**Params:** path: device_id (networkdeviceid)\n\n**Example:**\n```\ncurl http://localhost:5001/api/network/17\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "device_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - }, - "put": { - "tags": [ - "plugin-network" - ], - "summary": "Update asset + network-device fields; 409 on assetnumber/hostname conflicts; change-diff audit log; upserts primary IP...", - "description": "Update asset + network-device fields; 409 on assetnumber/hostname conflicts; change-diff audit log; upserts primary IP when ipaddress present\n\n**Auth:** jwt + permission:network.edit\n\n**Params:** body: any of assetnumber, name, serialnumber, gaugelabreference, maintenancereference, statusid, locationid, businessunitid, mapx, mapy, notes, isactive, networkdevicetypeid, vendorid, hostname, firmwareversion, portcount, ispoe, ismanaged, rackunit, ipaddress\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/network/17 -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"firmwareversion\":\"16.12.4\",\"ipaddress\":\"10.1.3.11\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "device_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: any of assetnumber, name, serialnumber, gaugelabreference, maintenancereference, statusid, locationid, businessunitid, mapx, mapy, notes, isactive, networkdevicetypeid, vendorid, hostname, firmwareversion, portcount, ispoe, ismanaged, rackunit, ipaddress" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-network" - ], - "summary": "Soft-delete a network device (sets asset.isactive=false; audit-logged)", - "description": "Soft-delete a network device (sets asset.isactive=false; audit-logged)\n\n**Auth:** jwt + permission:network.delete\n\n**Params:** path: device_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/network/17 -H \"Authorization: Bearer $TOKEN\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "device_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - } - }, - "/api/network/by-asset/{asset_id}": { - "get": { - "tags": [ - "plugin-network" - ], - "summary": "Look up a network device by its core assetid", - "description": "Look up a network device by its core assetid\n\n**Auth:** jwt-optional\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/network/by-asset/1042\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - } - }, - "/api/network/by-hostname/{hostname}": { - "get": { - "tags": [ - "plugin-network" - ], - "summary": "Look up a network device by exact hostname", - "description": "Look up a network device by exact hostname\n\n**Auth:** jwt-optional\n\n**Params:** path: hostname\n\n**Example:**\n```\ncurl http://localhost:5001/api/network/by-hostname/wjf-sw-idf3-01\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "hostname", + "name": "token", "in": "path", "required": true, "schema": { "type": "string" } } - ] - } - }, - "/api/network/dashboard/summary": { - "get": { - "tags": [ - "plugin-network" - ], - "summary": "Dashboard counts: total active devices, by type, by vendor, PoE vs non-PoE", - "description": "Dashboard counts: total active devices, by type, by vendor, PoE vs non-PoE\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/network/dashboard/summary\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/network/vlans": { - "get": { - "tags": [ - "plugin-network" - ], - "summary": "List VLANs, paginated, ordered by vlannumber", - "description": "List VLANs, paginated, ordered by vlannumber\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active (default true), search (name/description/vlannumber), type (exact vlantype)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/network/vlans?search=voice'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "plugin-network" - ], - "summary": "Create a VLAN; 409 on duplicate vlannumber; audit-logged", - "description": "Create a VLAN; 409 on duplicate vlannumber; audit-logged\n\n**Auth:** jwt + permission:network.create\n\n**Params:** body: vlannumber (required), name (required), description, vlantype\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/network/vlans -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"vlannumber\":120,\"name\":\"Shopfloor\",\"vlantype\":\"production\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: vlannumber (required), name (required), description, vlantype" - } - } - } - } - } - }, - "/api/network/vlans/{vlan_id}": { - "get": { - "tags": [ - "plugin-network" - ], - "summary": "Get one VLAN including its active subnets", - "description": "Get one VLAN including its active subnets\n\n**Auth:** jwt-optional\n\n**Params:** path: vlan_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/network/vlans/5\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "vlan_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - }, - "put": { - "tags": [ - "plugin-network" - ], - "summary": "Update a VLAN; 409 on vlannumber conflict; change-diff audit log", - "description": "Update a VLAN; 409 on vlannumber conflict; change-diff audit log\n\n**Auth:** jwt + permission:network.edit\n\n**Params:** body: vlannumber, name, description, vlantype, isactive\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/network/vlans/5 -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"description\":\"CNC cell VLAN\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "vlan_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } ], "requestBody": { "required": true, @@ -16565,297 +26586,11 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: vlannumber, name, description, vlantype, isactive" + "description": "path: token (string); body: JSON (fields not yet documented)" } } } } - }, - "delete": { - "tags": [ - "plugin-network" - ], - "summary": "Soft-delete a VLAN; 400 if it still has active subnets; audit-logged", - "description": "Soft-delete a VLAN; 400 if it still has active subnets; audit-logged\n\n**Auth:** jwt + permission:network.delete\n\n**Params:** path: vlan_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/network/vlans/5 -H \"Authorization: Bearer $TOKEN\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "vlan_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - } - }, - "/api/network/subnets": { - "get": { - "tags": [ - "plugin-network" - ], - "summary": "List subnets, paginated, ordered by cidr", - "description": "List subnets, paginated, ordered by cidr\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active (default true), search (cidr/name/description), vlanid, locationid, type (exact subnettype)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/network/subnets?vlanid=5'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "plugin-network" - ], - "summary": "Create a subnet; validates CIDR notation and vlanid existence; 409 on duplicate cidr; audit-logged", - "description": "Create a subnet; validates CIDR notation and vlanid existence; 409 on duplicate cidr; audit-logged\n\n**Auth:** jwt + permission:network.create\n\n**Params:** body: cidr (required, must contain '/'), name (required), description, gatewayip, subnetmask, networkaddress, broadcastaddress, vlanid, subnettype, locationid, dhcpenabled (default true), dhcprangestart, dhcprangeend, dns1, dns2\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/network/subnets -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"cidr\":\"10.1.3.0/24\",\"name\":\"IDF3 access\",\"vlanid\":5,\"gatewayip\":\"10.1.3.1\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: cidr (required, must contain '/'), name (required), description, gatewayip, subnetmask, networkaddress, broadcastaddress, vlanid, subnettype, locationid, dhcpenabled (default true), dhcprangestart, dhcprangeend, dns1, dns2" - } - } - } - } - } - }, - "/api/network/subnets/{subnet_id}": { - "get": { - "tags": [ - "plugin-network" - ], - "summary": "Get one subnet plus 'devices': every asset of any type (PC/printer/network) whose primary-IP Communication falls inside...", - "description": "Get one subnet plus 'devices': every asset of any type (PC/printer/network) whose primary-IP Communication falls inside the CIDR, with cross-plugin detail URLs\n\n**Auth:** jwt-optional\n\n**Params:** path: subnet_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/network/subnets/2\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "subnet_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - }, - "put": { - "tags": [ - "plugin-network" - ], - "summary": "Update a subnet; 409 on cidr conflict; change-diff audit log", - "description": "Update a subnet; 409 on cidr conflict; change-diff audit log\n\n**Auth:** jwt + permission:network.edit\n\n**Params:** body: any of cidr, name, description, gatewayip, subnetmask, networkaddress, broadcastaddress, vlanid, subnettype, locationid, dhcpenabled, dhcprangestart, dhcprangeend, dns1, dns2, isactive\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/network/subnets/2 -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"dhcpenabled\":false}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "subnet_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: any of cidr, name, description, gatewayip, subnetmask, networkaddress, broadcastaddress, vlanid, subnettype, locationid, dhcpenabled, dhcprangestart, dhcprangeend, dns1, dns2, isactive" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-network" - ], - "summary": "Soft-delete a subnet (isactive=false); audit-logged", - "description": "Soft-delete a subnet (isactive=false); audit-logged\n\n**Auth:** jwt + permission:network.delete\n\n**Params:** path: subnet_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/network/subnets/2 -H \"Authorization: Bearer $TOKEN\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "subnet_id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] } }, "/api/usb": { @@ -16863,10 +26598,9 @@ "tags": [ "plugin-usb" ], - "summary": "List USB devices with checkout status (paginated); dual-mode: selfhosted tables or external cmmc_usb DB per...", - "description": "List USB devices with checkout status (paginated); dual-mode: selfhosted tables or external cmmc_usb DB per usb_directory_mode setting\n\n**Auth:** jwt-optional\n\n**Params:** query: page, per_page, status=available|checkedout|retired, search (matches device_id or device_desc)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/usb?status=available&search=kingston&page=1&per_page=25' -H \"Authorization: Bearer $JWT\"\n```", + "summary": "List USB devices with checkout status.", + "description": "List USB devices with checkout status.\n\n**Auth:** permission:usb.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/usb -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -16887,6 +26621,12 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." } } }, @@ -16894,8 +26634,8 @@ "tags": [ "plugin-usb" ], - "summary": "Create a device (starts in checked-in status); 409 on duplicate device_id", - "description": "Create a device (starts in checked-in status); 409 on duplicate device_id\n\n**Auth:** jwt + permission:usb.create\n\n**Params:** body JSON: device_id (required), device_desc, device_owner (badge), locker_location\n\n**Example:**\n```\ncurl -X POST 'http://localhost:5001/api/usb' -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"device_id\":\"USB-0042\",\"device_desc\":\"Kingston 32GB\",\"device_owner\":\"212345678\",\"locker_location\":\"A3\"}'\n```", + "summary": "Add a device (starts checked-in).", + "description": "Add a device (starts checked-in).\n\n**Auth:** permission:usb.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/usb -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -16932,7 +26672,7 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body JSON: device_id (required), device_desc, device_owner (badge), locker_location" + "description": "body: JSON (fields not yet documented)" } } } @@ -16944,10 +26684,9 @@ "tags": [ "plugin-usb" ], - "summary": "Get one device plus its last 20 check-in/out log rows; 404 if unknown", - "description": "Get one device plus its last 20 check-in/out log rows; 404 if unknown\n\n**Auth:** jwt-optional\n\n**Params:** path: device_id\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/usb/USB-0042' -H \"Authorization: Bearer $JWT\"\n```", + "summary": "Get a single device plus its last 20 check-in/out log rows.", + "description": "Get a single device plus its last 20 check-in/out log rows.\n\n**Auth:** permission:usb.view\n\n**Params:** path: device_id (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/usb/x -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -16969,6 +26708,12 @@ } } }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, "404": { "description": "No such record." } @@ -16988,8 +26733,8 @@ "tags": [ "plugin-usb" ], - "summary": "Edit device fields (device_desc / device_owner / locker_location / status); 404 if unknown", - "description": "Edit device fields (device_desc / device_owner / locker_location / status); 404 if unknown\n\n**Auth:** jwt + permission:usb.edit\n\n**Params:** path: device_id; body JSON: any of device_desc, device_owner, locker_location, status\n\n**Example:**\n```\ncurl -X PUT 'http://localhost:5001/api/usb/USB-0042' -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"device_desc\":\"Kingston 64GB\",\"locker_location\":\"B1\"}'\n```", + "summary": "Edit device_desc / device_owner / locker_location / status.", + "description": "Edit device_desc / device_owner / locker_location / status.\n\n**Auth:** permission:usb.edit\n\n**Params:** path: device_id (string); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/usb/x -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -17039,133 +26784,7 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "path: device_id; body JSON: any of device_desc, device_owner, locker_location, status" - } - } - } - } - } - }, - "/api/usb/{device_id}/retire": { - "post": { - "tags": [ - "plugin-usb" - ], - "summary": "Retire a device (sets status to retired); 404 if unknown", - "description": "Retire a device (sets status to retired); 404 if unknown\n\n**Auth:** jwt + permission:usb.edit\n\n**Params:** path: device_id; no body\n\n**Example:**\n```\ncurl -X POST 'http://localhost:5001/api/usb/USB-0042/retire' -H \"Authorization: Bearer $JWT\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "device_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: device_id; no body" - } - } - } - } - } - }, - "/api/usb/{device_id}/checkout": { - "post": { - "tags": [ - "plugin-usb" - ], - "summary": "Check a device out to a badge (writes check-out log row, sets status checked-out, auto-creates user from HR directory)...", - "description": "Check a device out to a badge (writes check-out log row, sets status checked-out, auto-creates user from HR directory); 409 if already checked out\n\n**Auth:** jwt + permission:usb.create\n\n**Params:** path: device_id; body JSON: badge (required), locker_location (optional, also updates device)\n\n**Example:**\n```\ncurl -X POST 'http://localhost:5001/api/usb/USB-0042/checkout' -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"badge\":\"212345678\",\"locker_location\":\"A3\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "device_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: device_id; body JSON: badge (required), locker_location (optional, also updates device)" + "description": "path: device_id (string); body: JSON (fields not yet documented)" } } } @@ -17177,8 +26796,8 @@ "tags": [ "plugin-usb" ], - "summary": "Check a device back in (writes check-in log row with sanitized/virus-scan flags, sets status checked-in); 400 if not...", - "description": "Check a device back in (writes check-in log row with sanitized/virus-scan flags, sets status checked-in); 400 if not currently checked out\n\n**Auth:** jwt + permission:usb.create\n\n**Params:** path: device_id; body JSON: badge (required), locker_location, sanitized (bool/1/0), scanned_viruses (bool/1/0)\n\n**Example:**\n```\ncurl -X POST 'http://localhost:5001/api/usb/USB-0042/checkin' -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"badge\":\"212345678\",\"sanitized\":true,\"scanned_viruses\":true}'\n```", + "summary": "Check a device back in from a badge.", + "description": "Check a device back in from a badge.\n\n**Auth:** permission:usb.create\n\n**Params:** path: device_id (string); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/usb/x/checkin -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -17228,7 +26847,70 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "path: device_id; body JSON: badge (required), locker_location, sanitized (bool/1/0), scanned_viruses (bool/1/0)" + "description": "path: device_id (string); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/usb/{device_id}/checkout": { + "post": { + "tags": [ + "plugin-usb" + ], + "summary": "Check a device out to a badge.", + "description": "Check a device out to a badge.\n\n**Auth:** permission:usb.create\n\n**Params:** path: device_id (string); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/usb/x/checkout -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "device_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: device_id (string); body: JSON (fields not yet documented)" } } } @@ -17240,10 +26922,9 @@ "tags": [ "plugin-usb" ], - "summary": "Paginated check-in/out log for one device, newest first", - "description": "Paginated check-in/out log for one device, newest first\n\n**Auth:** jwt-optional\n\n**Params:** path: device_id; query: page, per_page\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/usb/USB-0042/history?page=1&per_page=50' -H \"Authorization: Bearer $JWT\"\n```", + "summary": "Paginated check-in/out log for one device.", + "description": "Paginated check-in/out log for one device.\n\n**Auth:** permission:usb.view\n\n**Params:** path: device_id (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/usb/x/history -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -17265,6 +26946,12 @@ } } }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, "404": { "description": "No such record." } @@ -17281,15 +26968,14 @@ ] } }, - "/api/usb/checkouts": { - "get": { + "/api/usb/{device_id}/retire": { + "post": { "tags": [ "plugin-usb" ], - "summary": "List check-out log rows (paginated), each with the device's current status joined in", - "description": "List check-out log rows (paginated), each with the device's current status joined in\n\n**Auth:** jwt-optional\n\n**Params:** query: page, per_page, active=true (only rows whose device is still checked out), badge (filter by badge_number)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/usb/checkouts?active=true&badge=212345678' -H \"Authorization: Bearer $JWT\"\n```", + "summary": "Retire a device (status -> retired).", + "description": "Retire a device (status -> retired).\n\n**Auth:** permission:usb.edit\n\n**Params:** path: device_id (string); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/usb/x/retire -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ - {}, { "bearerAuth": [] } @@ -17310,6 +26996,75 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "device_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: device_id (string); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/usb/checkouts": { + "get": { + "tags": [ + "plugin-usb" + ], + "summary": "List check-out log rows.", + "description": "List check-out log rows.\n\n**Auth:** permission:usb.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/usb/checkouts -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." } } } @@ -17319,10 +27074,9 @@ "tags": [ "plugin-usb" ], - "summary": "Latest check-out log row for every currently checked-out device (non-paginated list)", - "description": "Latest check-out log row for every currently checked-out device (non-paginated list)\n\n**Auth:** jwt-optional\n\n**Params:** query: badge (filter by badge_number)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/usb/checkouts/active?badge=212345678' -H \"Authorization: Bearer $JWT\"\n```", + "summary": "List the latest check-out log row for every currently checked-out device.", + "description": "List the latest check-out log row for every currently checked-out device.\n\n**Auth:** permission:usb.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/usb/checkouts/active -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -17343,6 +27097,102 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + } + } + } + }, + "/api/usb/kiosk/checkin": { + "post": { + "tags": [ + "plugin-usb" + ], + "summary": "Check a device in at the kiosk. Body: {device_id, badge, forsomeoneelse}.", + "description": "Check a device in at the kiosk. Body: {device_id, badge, forsomeoneelse}.\n\n**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)\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/usb/kiosk/checkin -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "apiKeyAuth": [] + }, + {} + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/usb/kiosk/checkout": { + "post": { + "tags": [ + "plugin-usb" + ], + "summary": "Check a device out at the kiosk. Body: {device_id, badge}.", + "description": "Check a device out at the kiosk. Body: {device_id, badge}.\n\n**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)\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/usb/kiosk/checkout -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "apiKeyAuth": [] + }, + {} + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "body: JSON (fields not yet documented)" + } + } } } } @@ -17352,9 +27202,14 @@ "tags": [ "plugin-usb" ], - "summary": "What the logged-out kiosk shows after a drive is scanned: description, checked-in/out status, locker, and the NAME of...", - "description": "What the logged-out kiosk shows after a drive is scanned: description, checked-in/out status, locker, and the NAME of the current holder (needed for the return-for-someone-else confirm; the holder's badge is never returned). 404 when the kiosk setting is off, so a disabled kiosk does not advertise the route\n\n**Auth:** none (kiosk; requires usb_kiosk_enabled)\n\n**Params:** path: device_id (the code on the drive label)\n\n**Example:**\n```\ncurl http://localhost:5001/api/usb/kiosk/device/USB-1\n```", - "security": [], + "summary": "What the kiosk shows after a device is scanned.", + "description": "What the kiosk shows after a device is scanned.\n\n**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)\n\n**Params:** path: device_id (string)\n\n**Example:**\n```\ncurl http://localhost:5001/api/usb/kiosk/device/x\n```", + "security": [ + { + "apiKeyAuth": [] + }, + {} + ], "responses": { "200": { "description": "Success. Body is the success_response envelope: {status, data, meta}.", @@ -17388,14 +27243,19 @@ ] } }, - "/api/usb/kiosk/checkout": { + "/api/usb/kiosk/others": { "post": { "tags": [ "plugin-usb" ], - "summary": "Check a drive out at the kiosk, attributed to the scanned badge. A badge that resolves to no employee is refused with...", - "description": "Check a drive out at the kiosk, attributed to the scanned badge. A badge that resolves to no employee is refused with 422 (unlike the signed-in route, which creates the user row). 409 when the drive is already out\n\n**Auth:** none (kiosk; requires usb_kiosk_enabled)\n\n**Params:** body: device_id, badge\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/usb/kiosk/checkout -H 'Content-Type: application/json' -d '{\"device_id\":\"USB-1\",\"badge\":\"270015376\"}'\n```", - "security": [], + "summary": "Drives checked out to OTHER people, with holder names. Body: {badge}.", + "description": "Drives checked out to OTHER people, with holder names. Body: {badge}.\n\n**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)\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/usb/kiosk/others -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "apiKeyAuth": [] + }, + {} + ], "responses": { "200": { "description": "Success. Body is the success_response envelope: {status, data, meta}.", @@ -17421,21 +27281,25 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: device_id, badge" + "description": "body: JSON (fields not yet documented)" } } } } } }, - "/api/usb/kiosk/checkin": { - "post": { + "/api/usb/kiosk/provisioning-link": { + "get": { "tags": [ "plugin-usb" ], - "summary": "Return a drive at the kiosk. Holder by default: a badge that does not match the current holder gets 409 with...", - "description": "Return a drive at the kiosk. Holder by default: a badge that does not match the current holder gets 409 with details.needsconfirm and the holder's name, and the caller re-posts with forsomeoneelse=true. The badge RECORDED is the one scanned, so the log shows who handed it back. Set usb_kiosk_allow_return_for_others=false to refuse a mismatch with 403 instead\n\n**Auth:** none (kiosk; requires usb_kiosk_enabled)\n\n**Params:** body: device_id, badge, forsomeoneelse (optional bool)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/usb/kiosk/checkin -H 'Content-Type: application/json' -d '{\"device_id\":\"USB-1\",\"badge\":\"270015376\"}'\n```", - "security": [], + "summary": "The URL that provisions a screen, token included, for an administrator.", + "description": "The URL that provisions a screen, token included, for an administrator.\n\n**Auth:** permission:settings.edit\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/usb/kiosk/provisioning-link -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], "responses": { "200": { "description": "Success. Body is the success_response envelope: {status, data, meta}.", @@ -17452,18 +27316,12 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: device_id, badge, forsomeoneelse (optional bool)" - } - } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." } } } @@ -17473,9 +27331,14 @@ "tags": [ "plugin-usb" ], - "summary": "What one badge may do at the kiosk: the caller's own drives (to return), the drives that are free (to take), and a...", - "description": "What one badge may do at the kiosk: the caller's own drives (to return), the drives that are free (to take), and a COUNT of everything else. The drives carry no barcode so the kiosk must offer a list; requiring a badge that resolves to a real person keeps that list from being anonymous, and a POST keeps the badge out of proxy access logs. 422 for an unresolvable badge\n\n**Auth:** none (kiosk; requires usb_kiosk_enabled)\n\n**Params:** body: badge\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/usb/kiosk/session -H 'Content-Type: application/json' -d '{\"badge\":\"270015376\"}'\n```", - "security": [], + "summary": "What one badge may do right now. Body: {badge}.", + "description": "What one badge may do right now. Body: {badge}.\n\n**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)\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/usb/kiosk/session -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "apiKeyAuth": [] + }, + {} + ], "responses": { "200": { "description": "Success. Body is the success_response envelope: {status, data, meta}.", @@ -17501,21 +27364,25 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: badge" + "description": "body: JSON (fields not yet documented)" } } } } } }, - "/api/usb/kiosk/others": { - "post": { + "/api/usb/kiosk/setup": { + "get": { "tags": [ "plugin-usb" ], - "summary": "Drives checked out to OTHER people, with holder names, so somebody can return one left behind. Behind its own step...", - "description": "Drives checked out to OTHER people, with holder names, so somebody can return one left behind. Behind its own step rather than on the main kiosk screen, and 404 when usb_kiosk_allow_return_for_others is off - there is no point naming holders of drives the kiosk will not let anyone return\n\n**Auth:** none (kiosk; requires usb_kiosk_enabled)\n\n**Params:** body: badge\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/usb/kiosk/others -H 'Content-Type: application/json' -d '{\"badge\":\"270015376\"}'\n```", - "security": [], + "summary": "Trade a setup code for this browser's kiosk cookie, then open the kiosk.", + "description": "Trade a setup code for this browser's kiosk cookie, then open the kiosk.\n\n**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)\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/usb/kiosk/setup\n```", + "security": [ + { + "setupCodeAuth": [] + } + ], "responses": { "200": { "description": "Success. Body is the success_response envelope: {status, data, meta}.", @@ -17532,18 +27399,12 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: badge" - } - } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." } } } @@ -17553,1694 +27414,8 @@ "tags": [ "plugin-usb" ], - "summary": "Removable-media audit: every check-out and check-in in the window, newest first, with who, which drive, when and which...", - "description": "Removable-media audit: every check-out and check-in in the window, newest first, with who, which drive, when and which locker - plus summary counts. Rows rather than totals, because an audit asks who had a specific drive on a specific day. Works against the external cmmc_usb log or the self-hosted tables; in self-hosted mode one stored loan expands into a take and a return so a returned drive is distinguishable from one still out\n\n**Auth:** jwt + usb.view\n\n**Params:** query: period (day|month|sixmonths|year|all, default month) or days= (0 = all time); format=csv for a download\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/usb/reports/audit?period=year' -H \"Authorization: Bearer $TOKEN\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/printers/types": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "List printer types, paginated, with active filter and name search.", - "description": "List printer types, paginated, with active filter and name search.\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active (default true; 'false' includes inactive), search\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/printers/types?search=laser&active=false'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "plugin-printers" - ], - "summary": "Create a new printer type (or reactivate an inactive duplicate).", - "description": "Create a new printer type (or reactivate an inactive duplicate).\n\n**Auth:** permission:printers.create\n\n**Params:** body: printertype (required), description, icon, color; reactivates a matching inactive type instead of 409\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"printertype\":\"Label\",\"color\":\"#00f\"}' http://localhost:5001/api/printers/types\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: printertype (required), description, icon, color; reactivates a matching inactive type instead of 409" - } - } - } - } - } - }, - "/api/printers/types/{type_id}": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "Get a single printer type by ID.", - "description": "Get a single printer type by ID.\n\n**Auth:** jwt-optional\n\n**Params:** path: type_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/types/3\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "plugin-printers" - ], - "summary": "Update a printer type.", - "description": "Update a printer type.\n\n**Auth:** permission:printers.edit\n\n**Params:** body: printertype, description, icon, color, isactive (any subset); 409 on name clash\n\n**Example:**\n```\ncurl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"isactive\":false}' http://localhost:5001/api/printers/types/3\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: printertype, description, icon, color, isactive (any subset); 409 on name clash" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-printers" - ], - "summary": "Hard-delete a printer type when unused.", - "description": "Hard-delete a printer type when unused.\n\n**Auth:** permission:printers.delete\n\n**Params:** path: type_id; 409 if any printer still references the type\n\n**Example:**\n```\ncurl -X DELETE -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/types/3\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "type_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/printers/drivers": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "List printer driver packages (named SMB/HTTP links).", - "description": "List printer driver packages (named SMB/HTTP links).\n\n**Auth:** jwt-optional\n\n**Params:** active (default true; 'false' includes inactive). Unpaginated.\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/printers/drivers?active=false'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "plugin-printers" - ], - "summary": "Create a driver entry.", - "description": "Create a driver entry.\n\n**Auth:** permission:printers.create\n\n**Params:** body: name (required), location (required), description, modelnumberid, isactive\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"name\":\"HP UPD\",\"location\":\"\\\\\\\\fileserver\\\\drivers\\\\hpupd\"}' http://localhost:5001/api/printers/drivers\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: name (required), location (required), description, modelnumberid, isactive" - } - } - } - } - } - }, - "/api/printers/drivers/{driver_id}": { - "put": { - "tags": [ - "plugin-printers" - ], - "summary": "Update a driver entry.", - "description": "Update a driver entry.\n\n**Auth:** permission:printers.edit\n\n**Params:** body: name, location, description, isactive, modelnumberid (any subset)\n\n**Example:**\n```\ncurl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"isactive\":false}' http://localhost:5001/api/printers/drivers/5\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "driver_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: name, location, description, isactive, modelnumberid (any subset)" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-printers" - ], - "summary": "Hard-delete a driver entry.", - "description": "Hard-delete a driver entry.\n\n**Auth:** permission:printers.delete\n\n**Params:** path: driver_id\n\n**Example:**\n```\ncurl -X DELETE -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/drivers/5\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "driver_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/printers": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "List printers (joined Asset+Printer) with filters, search, sorting, pagination; each row includes primary IP.", - "description": "List printers (joined Asset+Printer) with filters, search, sorting, pagination; each row includes primary IP.\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, active, assetnumber (exact-match for import idempotency), search (assetnumber/name/serial/hostname/windowsname/gaugelabreference/maintenancereference), typeid|type_id, vendorid|vendor_id, locationid|location_id, businessunitid|businessunit_id, sort (hostname|assetnumber|name), dir (asc|desc)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/printers?search=csf&typeid=2&sort=assetnumber&dir=desc'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "plugin-printers" - ], - "summary": "Create a printer (Asset + Printer extension + optional primary IP communication).", - "description": "Create a printer (Asset + Printer extension + optional primary IP communication).\n\n**Auth:** permission:printers.create\n\n**Params:** body: assetnumber (required); name, serialnumber, gaugelabreference, maintenancereference, statusid, locationid, businessunitid, printertypeid, vendorid, modelnumberid, hostname, windowsname, sharename, iscsf, installpath, pin, iscolor, isduplex, isnetwork, mapx, mapy, notes, ipaddress (creates primary IP comm); X-Import-Mode header preserves legacy timestamps\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"assetnumber\":\"PR-0042\",\"hostname\":\"wjprn042\",\"ipaddress\":\"10.1.2.42\",\"printertypeid\":1}' http://localhost:5001/api/printers\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: assetnumber (required); name, serialnumber, gaugelabreference, maintenancereference, statusid, locationid, businessunitid, printertypeid, vendorid, modelnumberid, hostname, windowsname, sharename, iscsf, installpath, pin, iscolor, isduplex, isnetwork, mapx, mapy, notes, ipaddress (creates primary IP comm); X-Import-Mode header preserves legacy timestamps" - } - } - } - } - } - }, - "/api/printers/install-list": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "Flat unpaginated list of active network printers (with map positions) for the signed printer-installer EXE; replaces...", - "description": "Flat unpaginated list of active network printers (with map positions) for the signed printer-installer EXE; replaces classic apiprinters.asp.\n\n**Auth:** jwt-optional\n\n**Params:** format=text for pipe-delimited installer variant (printerid|windowsname|vendorname|modelnumber|hostname|ipaddress|mapx|mapy); default JSON\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/printers/install-list?format=text'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/printers/install-batch": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "Generate and download a Windows .bat that installs selected printers (HP/Xerox via universal PrinterInstaller.exe...", - "description": "Generate and download a Windows .bat that installs selected printers (HP/Xerox via universal PrinterInstaller.exe, per-printer .exe /SILENT, or flags manual).\n\n**Auth:** jwt-optional\n\n**Params:** printerids (required, comma-separated printer IDs)\n\n**Example:**\n```\ncurl -OJ 'http://localhost:5001/api/printers/install-batch?printerids=1,2,3'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/printers/pc-default": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "Look up a PC's default printer via the defaultprinter asset relationship (parity with classic apipcdefaultprinter.asp)...", - "description": "Look up a PC's default printer via the defaultprinter asset relationship (parity with classic apipcdefaultprinter.asp); used by installer to preselect map hotspot.\n\n**Auth:** jwt-optional\n\n**Params:** machine (PC asset number), format=text for pipe-delimited variant; returns {} / empty body if no default set\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/printers/pc-default?machine=0421&format=text'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/printers/for-host/{hostname}": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "Desired printer set for one PC: its own usesprinter/defaultprinter relationships, or, when it has none, the ones...", - "description": "Desired printer set for one PC: its own usesprinter/defaultprinter relationships, or, when it has none, the ones inherited through its controls edge to the machine it drives. Each entry carries what a client needs to install the queue (queue name, hostname, ipaddress, port, drivername, driverlocation, isdefault, inherited). A known host with nothing assigned returns an empty list and a null default (the client's designed no-op); an unknown hostname, or a site without the computers plugin, is a 404.\n\n**Auth:** jwt-optional\n\n**Params:** path: hostname (matched case-insensitively, as sent by the client's COMPUTERNAME); no query parameters\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/for-host/workstation01\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "hostname", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/printers/observed/{hostname}": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "The observed half of the loop and the mirror of /for-host: what this bay last REPORTED it has (POST...", - "description": "The observed half of the loop and the mirror of /for-host: what this bay last REPORTED it has (POST /api/collector/printers), each queue judged against the resolved assignment it SHOULD have. Read-only - nothing here changes an assignment, however wrong the bay looks. Every queue is classified matching / drifted (same printer, different port address or driver) / extra (a printer ShopDB knows, installed unassigned) / missing (assigned, not reported) / unknown (matches no printer, never guessed), each entry carrying both sides plus driftfields; the response adds observedat, the assignment source (own rows or the machine this PC controls), a per-classification summary, and a seedcandidate preview of what a seed would write. 404 only when the hostname has neither a report nor a computer record; a known PC that never reported returns an empty queue list.\n\n**Auth:** permission:printers.view\n\n**Params:** path: hostname (case-insensitive, short name or FQDN, as the reporting client sent it); no query parameters\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/observed/workstation01\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "hostname", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/printers/observed/for-asset/{asset_id}": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "The same observed-against-assigned comparison as /api/printers/observed/, reached from an asset page, with...", - "description": "The same observed-against-assigned comparison as /api/printers/observed/, reached from an asset page, with one block per reporting host. Assigned state lives on the MACHINE while observed state is reported by the PCs, so a machine answers with a block for each active PC that controls it (its own hostname first when the asset is itself a PC); blocks rather than one merged list because two PCs legitimately share one machine and the actionable part of drift is which box to walk to. Each block carries the same fields as the by-hostname route: queues classified matching / drifted / extra / missing / unknown, observedat, the assignment source, a summary, and a seedcandidate preview. Read-only. An asset nothing has reported for is an empty hosts list and a 200, not a 404.\n\n**Auth:** permission:printers.view\n\n**Params:** path: asset_id (a machine or a PC); no query parameters\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/observed/for-asset/312\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/printers/assignments/for-asset/{asset_id}": { - "put": { - "tags": [ - "plugin-printers" - ], - "summary": "Reconcile an asset's whole printer assignment in one write: soft-deletes usesprinter rows that went away, reactivates...", - "description": "Reconcile an asset's whole printer assignment in one write: soft-deletes usesprinter rows that went away, reactivates previously removed ones, creates new ones, and replaces the single defaultprinter row (exactly one per asset, optional, always one of the assigned printers). Removing an assignment has no side effects and never uninstalls anything on a client.\n\n**Auth:** permission:printers.edit\n\n**Params:** path: asset_id (the machine or PC asset the printers belong to); body: printerassetids (list of printer asset IDs, required, empty list clears the assignment), defaultprinterassetid (int or null; 400 unless it is one of printerassetids)\n\n**Example:**\n```\ncurl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"printerassetids\":[204,205],\"defaultprinterassetid\":204}' http://localhost:5001/api/printers/assignments/for-asset/312\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: asset_id (the machine or PC asset the printers belong to); body: printerassetids (list of printer asset IDs, required, empty list clears the assignment), defaultprinterassetid (int or null; 400 unless it is one of printerassetids)" - } - } - } - } - }, - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "This asset's OWN printer assignment - printerassetids and defaultprinterassetid - deliberately WITHOUT inheritance. The...", - "description": "This asset's OWN printer assignment - printerassetids and defaultprinterassetid - deliberately WITHOUT inheritance. The editor has to show what this asset's own rows say, or a machine's printers would appear ticked on the PC that inherits them and unticking one would silently create an override. /printers/for-host is the resolved view the client uses; this is the editable one\n\n**Auth:** optional jwt\n\n**Params:** asset_id in path\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/assignments/for-asset/14\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/printers/assignments/seed-from-observed/{asset_id}": { - "post": { - "tags": [ - "plugin-printers" - ], - "summary": "The ONE path from observed state to assigned state, and a person has to ask for it: no collector, cycle or background...", - "description": "The ONE path from observed state to assigned state, and a person has to ask for it: no collector, cycle or background job reaches this route, so a bay that installed the wrong printer can never make itself right by reporting it. Writes the matched observed queues through the same reconcile as PUT /printers/assignments/for-asset (usesprinter rows plus the single defaultprinter, set only when the observed default itself matched a printer). Returns what was written, the skipped queues, and warnings - including one when the target controls another asset, because its own rows then shadow that machine's assignment for good. Normally posted against the MACHINE so the assignment survives a reimage.\n\n**Auth:** permission:printers.edit\n\n**Params:** path: asset_id (the machine or PC to write the assignment on); body, all optional: hostname (which report to seed from; omitted, the asset's own hostname or its single controlling PC), allowunmatched (bool, seed the matched queues anyway when some queue matches no printer). 404 when no named or derived host has reported; 409 when several controlling PCs have reported, or when any queue matches no printer and allowunmatched is unset; 400 when nothing matched, since writing the empty set would silently unassign the asset. Nothing is written on any of those.\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"hostname\":\"workstation01\"}' http://localhost:5001/api/printers/assignments/seed-from-observed/312\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: asset_id (the machine or PC to write the assignment on); body, all optional: hostname (which report to seed from; omitted, the asset's own hostname or its single controlling PC), allowunmatched (bool, seed the matched queues anyway when some queue matches no printer). 404 when no named or derived host has reported; 409 when several controlling PCs have reported, or when any queue matches no printer and allowunmatched is unset; 400 when nothing matched, since writing the empty set would silently unassign the asset. Nothing is written on any of those." - } - } - } - } - } - }, - "/api/printers/{printer_id}": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "Get one printer with full asset details, communications, and active drivers matching its model.", - "description": "Get one printer with full asset details, communications, and active drivers matching its model.\n\n**Auth:** jwt-optional\n\n**Params:** path: printer_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/17\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "printer_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "plugin-printers" - ], - "summary": "Update printer: asset, extension, and primary IP in one call.", - "description": "Update printer: asset, extension, and primary IP in one call.\n\n**Auth:** permission:printers.edit\n\n**Params:** body: any subset of asset fields (assetnumber, name, serialnumber, gaugelabreference, maintenancereference, statusid, locationid, businessunitid, mapx, mapy, notes, isactive) + printer fields (printertypeid, vendorid, modelnumberid, hostname, windowsname, sharename, iscsf, installpath, pin, iscolor, isduplex, isnetwork) + ipaddress (upserts/clears primary IP comm); 409 on assetnumber clash\n\n**Example:**\n```\ncurl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"ipaddress\":\"10.1.2.99\",\"iscolor\":true}' http://localhost:5001/api/printers/17\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "printer_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: any subset of asset fields (assetnumber, name, serialnumber, gaugelabreference, maintenancereference, statusid, locationid, businessunitid, mapx, mapy, notes, isactive) + printer fields (printertypeid, vendorid, modelnumberid, hostname, windowsname, sharename, iscsf, installpath, pin, iscolor, isduplex, isnetwork) + ipaddress (upserts/clears primary IP comm); 409 on assetnumber clash" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-printers" - ], - "summary": "Soft-delete a printer (sets the underlying asset isactive=false).", - "description": "Soft-delete a printer (sets the underlying asset isactive=false).\n\n**Auth:** permission:printers.delete\n\n**Params:** path: printer_id\n\n**Example:**\n```\ncurl -X DELETE -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/17\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "printer_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/printers/by-asset/{asset_id}": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "Get printer data keyed by core asset ID.", - "description": "Get printer data keyed by core asset ID.\n\n**Auth:** jwt-optional\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/by-asset/204\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/printers/{printer_id}/supplies": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "Real-time supply levels from Zabbix for one printer, annotated with status/color/part numbers; fails soft (empty...", - "description": "Real-time supply levels from Zabbix for one printer, annotated with status/color/part numbers; fails soft (empty supplies, pingstatus -1) when Zabbix is off/unreachable.\n\n**Auth:** jwt-optional\n\n**Params:** path: printer_id; needs an IP communication on the printer\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/17/supplies\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "printer_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/printers/lowsupplies": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "Fleet-wide report of printers with low/critical supply levels from Zabbix, with summary counts.", - "description": "Fleet-wide report of printers with low/critical supply levels from Zabbix, with summary counts.\n\n**Auth:** jwt-optional\n\n**Params:** none (data cached 5 min)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/lowsupplies\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/printers/lookup": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "Find a printer by IP/FQDN (parity with classic printerlookup.asp; used by Zabbix to deep-link to a printer record).", - "description": "Find a printer by IP/FQDN (parity with classic printerlookup.asp; used by Zabbix to deep-link to a printer record).\n\n**Auth:** printers.view\n\n**Params:** ip or fqdn (one required; ip matches a communication address, fqdn matches the printer's hostname)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/printers/lookup?ip=10.1.2.42'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/printers/supplies/refresh": { - "post": { - "tags": [ - "plugin-printers" - ], - "summary": "Clear the cached Zabbix supply data so next reads pull fresh values (toner-report Refresh button).", - "description": "Clear the cached Zabbix supply data so next reads pull fresh values (toner-report Refresh button).\n\n**Auth:** permission:printers.create\n\n**Params:** none\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/supplies/refresh\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "none" - } - } - } - } - } - }, - "/api/printers/dashboard/summary": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "Dashboard summary: total active printers, counts by type and vendor, low/critical supply counts (Zabbix, if reachable).", - "description": "Dashboard summary: total active printers, counts by type and vendor, low/critical supply counts (Zabbix, if reachable).\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/dashboard/summary\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/printers/supplies/meta": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "Allowed enum values for supplytype, color, and capacitytier (for the UI forms).", - "description": "Allowed enum values for supplytype, color, and capacitytier (for the UI forms).\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/supplies/meta\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/printers/models": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "List printer models with supply counts for the supply-management picker (restricted to models attached to printers or...", - "description": "List printer models with supply counts for the supply-management picker (restricted to models attached to printers or already carrying supplies).\n\n**Auth:** jwt-optional\n\n**Params:** page, per_page, search (modelnumber), vendorid|vendor_id, withsupplies=true (only models that already have supplies)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/printers/models?withsupplies=true&search=M404'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/printers/models/{modelnumberid}/supplies": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "List all active supplies (toner/drum/waste part numbers) mapped to a model.", - "description": "List all active supplies (toner/drum/waste part numbers) mapped to a model.\n\n**Auth:** jwt-optional\n\n**Params:** path: modelnumberid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/printers/models/12/supplies\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "modelnumberid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "post": { - "tags": [ - "plugin-printers" - ], - "summary": "Add a supply part-number mapping to a model.", - "description": "Add a supply part-number mapping to a model.\n\n**Auth:** permission:printers.create\n\n**Params:** body: partnumber (required), supplytype (default toner), color (default none), capacitytier (default standard), marketingname, pageyield, notes; 409 if part already mapped to model\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"partnumber\":\"CF259A\",\"supplytype\":\"toner\",\"color\":\"black\"}' http://localhost:5001/api/printers/models/12/supplies\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "modelnumberid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: partnumber (required), supplytype (default toner), color (default none), capacitytier (default standard), marketingname, pageyield, notes; 409 if part already mapped to model" - } - } - } - } - } - }, - "/api/printers/supplies/{modelsupplyid}": { - "put": { - "tags": [ - "plugin-printers" - ], - "summary": "Update a model supply mapping.", - "description": "Update a model supply mapping.\n\n**Auth:** permission:printers.edit\n\n**Params:** body: any subset of supplytype, color, capacitytier, partnumber, marketingname, pageyield, notes; enum-validated, 409 on partnumber clash within model\n\n**Example:**\n```\ncurl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"capacitytier\":\"high\"}' http://localhost:5001/api/printers/supplies/44\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "modelsupplyid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: any subset of supplytype, color, capacitytier, partnumber, marketingname, pageyield, notes; enum-validated, 409 on partnumber clash within model" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-printers" - ], - "summary": "Hard-delete a model supply mapping.", - "description": "Hard-delete a model supply mapping.\n\n**Auth:** permission:printers.delete\n\n**Params:** path: modelsupplyid\n\n**Example:**\n```\ncurl -X DELETE -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/supplies/44\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "modelsupplyid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/printers/supplies/forecast": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "One row per CARTRIDGE: days-to-empty from Zabbix history (hourly trends over a long window), soonest first, banded...", - "description": "One row per CARTRIDGE: days-to-empty from Zabbix history (hourly trends over a long window), soonest first, banded empty/soon/month/later, each with its part numbers and replacement count. Also returns orderlist - what to buy within horizondays, grouped by part number with a quantity. Cartridges with no honest estimate come back separately with the reason. available=false when Zabbix is unreachable\n\n**Auth:** jwt-optional\n\n**Params:** days (1-365, default 90)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/printers/supplies/forecast?days=90'\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/printers/dashboard/supplies": { - "get": { - "tags": [ - "plugin-printers" - ], - "summary": "Dashboard card: printers needing a cartridge, one row per printer. Reuses the low-supplies query and its five-minute...", - "description": "Dashboard card: printers needing a cartridge, one row per printer. Reuses the low-supplies query and its five-minute cache, so the card costs the same as the report\n\n**Auth:** permission:printers.view\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/dashboard/supplies\n```", + "summary": "Every check-out and check-in in the window, newest first.", + "description": "Every check-out and check-in in the window, newest first.\n\n**Auth:** permission:usb.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/usb/reports/audit -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -19277,10 +27452,9 @@ "tags": [ "plugin-warranty" ], - "summary": "List warranties with linked-asset summaries and derived status (batch asset fetch, ordered by enddate with null last).", - "description": "List warranties with linked-asset summaries and derived status (batch asset fetch, ordered by enddate with null last).\n\n**Auth:** jwt-optional\n\n**Params:** query: active (default true; 'false' includes inactive), servicetag (exact match, for idempotent import), vendor (exact match), assetid (int, filter to warranties covering that asset), status (post-filter on derived status: expired|expiring|active|unknown)\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/warranty?status=expiring&assetid=42'\n```", + "summary": "List warranties. Filters: ?status=, ?assetid=, ?active=false.", + "description": "List warranties. Filters: ?status=, ?assetid=, ?active=false.\n\n**Auth:** permission:warranty.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/warranty -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -19301,6 +27475,12 @@ "$ref": "#/components/schemas/ErrorEnvelope" } } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." } } }, @@ -19308,8 +27488,8 @@ "tags": [ "plugin-warranty" ], - "summary": "Create a warranty (vendor required) and optionally link it to assets; returns 201.", - "description": "Create a warranty (vendor required) and optionally link it to assets; returns 201.\n\n**Auth:** jwt + permission:warranty.create\n\n**Params:** JSON body: vendor (required), servicetag, provider (default 'manual', lowercased), servicelevel, startdate (YYYY-MM-DD), enddate (YYYY-MM-DD), notes, assetids (list of asset ids to link)\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"vendor\":\"Dell\",\"servicetag\":\"ABC1234\",\"enddate\":\"2027-06-30\",\"assetids\":[42]}' http://localhost:5001/api/warranty\n```", + "summary": "UNDOCUMENTED: warranty.create_warranty has no docstring.", + "description": "UNDOCUMENTED: warranty.create_warranty has no docstring.\n\n**Auth:** permission:warranty.create\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/warranty -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -19346,7 +27526,343 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "JSON body: vendor (required), servicetag, provider (default 'manual', lowercased), servicelevel, startdate (YYYY-MM-DD), enddate (YYYY-MM-DD), notes, assetids (list of asset ids to link)" + "description": "body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/warranty/{warrantyid}": { + "delete": { + "tags": [ + "plugin-warranty" + ], + "summary": "UNDOCUMENTED: warranty.delete_warranty has no docstring.", + "description": "UNDOCUMENTED: warranty.delete_warranty has no docstring.\n\n**Auth:** permission:warranty.delete\n\n**Params:** path: warrantyid (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/warranty/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "warrantyid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "get": { + "tags": [ + "plugin-warranty" + ], + "summary": "UNDOCUMENTED: warranty.get_warranty has no docstring.", + "description": "UNDOCUMENTED: warranty.get_warranty has no docstring.\n\n**Auth:** permission:warranty.view\n\n**Params:** path: warrantyid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/warranty/1 -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "warrantyid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "put": { + "tags": [ + "plugin-warranty" + ], + "summary": "UNDOCUMENTED: warranty.update_warranty has no docstring.", + "description": "UNDOCUMENTED: warranty.update_warranty has no docstring.\n\n**Auth:** permission:warranty.edit\n\n**Params:** path: warrantyid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/warranty/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "warrantyid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: warrantyid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/warranty/{warrantyid}/proof": { + "delete": { + "tags": [ + "plugin-warranty" + ], + "summary": "Remove a warranty's proof document.", + "description": "Remove a warranty's proof document.\n\n**Auth:** permission:warranty.edit\n\n**Params:** path: warrantyid (int)\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/warranty/1/proof -H \"Authorization: Bearer $TOK\"\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "warrantyid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "post": { + "tags": [ + "plugin-warranty" + ], + "summary": "Upload (or replace) the proof-of-cover document for a warranty.", + "description": "Upload (or replace) the proof-of-cover document for a warranty.\n\n**Auth:** permission:warranty.edit\n\n**Params:** path: warrantyid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/warranty/1/proof -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "warrantyid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: warrantyid (int); body: JSON (fields not yet documented)" + } + } + } + } + } + }, + "/api/warranty/{warrantyid}/refresh": { + "post": { + "tags": [ + "plugin-warranty" + ], + "summary": "UNDOCUMENTED: warranty.refresh_warranty has no docstring.", + "description": "UNDOCUMENTED: warranty.refresh_warranty has no docstring.\n\n**Auth:** permission:warranty.edit\n\n**Params:** path: warrantyid (int); body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/warranty/1/refresh -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success. Body is the success_response envelope: {status, data, meta}.", + "content": { + "application/json": { + "$ref": "#/components/schemas/SuccessEnvelope" + } + } + }, + "default": { + "description": "Error. Body is the error envelope; the code and message are nested under data.error.", + "content": { + "application/json": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, + "404": { + "description": "No such record." + } + }, + "parameters": [ + { + "name": "warrantyid", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "description": "path: warrantyid (int); body: JSON (fields not yet documented)" } } } @@ -19358,10 +27874,9 @@ "tags": [ "plugin-warranty" ], - "summary": "Active warranties covering one asset, for the asset-detail panel.", - "description": "Active warranties covering one asset, for the asset-detail panel.\n\n**Auth:** jwt-optional\n\n**Params:** path: assetid\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/warranty/asset/42\n```", + "summary": "Warranties covering one asset (for the asset-detail panel).", + "description": "Warranties covering one asset (for the asset-detail panel).\n\n**Auth:** permission:warranty.view\n\n**Params:** path: assetid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/warranty/asset/1 -H \"Authorization: Bearer $TOK\"\n```", "security": [ - {}, { "bearerAuth": [] } @@ -19383,6 +27898,12 @@ } } }, + "401": { + "description": "Missing or invalid credentials." + }, + "403": { + "description": "Authenticated, but not permitted." + }, "404": { "description": "No such record." } @@ -19399,478 +27920,13 @@ ] } }, - "/api/warranty/{warrantyid}": { - "get": { - "tags": [ - "plugin-warranty" - ], - "summary": "Fetch a single warranty by id with asset summaries; 404 if missing.", - "description": "Fetch a single warranty by id with asset summaries; 404 if missing.\n\n**Auth:** jwt-optional\n\n**Params:** path: warrantyid\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/warranty/7\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "warrantyid", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - }, - "put": { - "tags": [ - "plugin-warranty" - ], - "summary": "Partial update of any warranty field (only keys present in body change), including isactive and replacing asset links...", - "description": "Partial update of any warranty field (only keys present in body change), including isactive and replacing asset links via assetids.\n\n**Auth:** jwt + permission:warranty.edit\n\n**Params:** path: warrantyid; JSON body (all optional): vendor, servicetag, provider, servicelevel, startdate, enddate, notes, isactive (bool), assetids (full replacement list)\n\n**Example:**\n```\ncurl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"enddate\":\"2028-01-15\",\"assetids\":[42,43]}' http://localhost:5001/api/warranty/7\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "warrantyid", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: warrantyid; JSON body (all optional): vendor, servicetag, provider, servicelevel, startdate, enddate, notes, isactive (bool), assetids (full replacement list)" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-warranty" - ], - "summary": "Hard-delete a warranty (and its asset links); 404 if missing.", - "description": "Hard-delete a warranty (and its asset links); 404 if missing.\n\n**Auth:** jwt + permission:warranty.delete\n\n**Params:** path: warrantyid\n\n**Example:**\n```\ncurl -X DELETE -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/warranty/7\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "warrantyid", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ] - } - }, - "/api/warranty/{warrantyid}/refresh": { - "post": { - "tags": [ - "plugin-warranty" - ], - "summary": "Re-query the warranty's provider (dell etc.) by service tag and update servicelevel/startdate/enddate...", - "description": "Re-query the warranty's provider (dell etc.) by service tag and update servicelevel/startdate/enddate + lastcheckeddate; 400 if provider is manual or not configured.\n\n**Auth:** jwt + permission:warranty.edit\n\n**Params:** path: warrantyid; no body\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/warranty/7/refresh\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "warrantyid", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: warrantyid; no body" - } - } - } - } - } - }, - "/api/warranty/sync/dell": { - "post": { - "tags": [ - "plugin-warranty" - ], - "summary": "Bulk Dell sync: look up serials of active assets lacking a dated warranty via Dell bulk_lookup, creating or updating...", - "description": "Bulk Dell sync: look up serials of active assets lacking a dated warranty via Dell bulk_lookup, creating or updating (canonicalizing to provider dell) warranties; returns candidates/tags/matched/created/updated counts.\n\n**Auth:** jwt + permission:warranty.edit\n\n**Params:** query: all (default false; 'true' re-checks assets that already have a dated warranty); no body\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/warranty/sync/dell?all=true'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "query: all (default false; 'true' re-checks assets that already have a dated warranty); no body" - } - } - } - } - } - }, - "/api/warranty/report": { - "get": { - "tags": [ - "plugin-warranty" - ], - "summary": "Report for the Reports hub: active warranties bucketed by derived status (expired/expiring/active/unknown) with...", - "description": "Report for the Reports hub: active warranties bucketed by derived status (expired/expiring/active/unknown) with per-bucket counts and full lists.\n\n**Auth:** jwt-optional\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/warranty/report\n```", - "security": [ - {}, - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/warranty/{warrantyid}/proof": { - "post": { - "tags": [ - "plugin-warranty" - ], - "summary": "Upload or replace the proof-of-cover document (invoice, certificate)", - "description": "Upload or replace the proof-of-cover document (invoice, certificate)\n\n**Auth:** jwt + warranty.edit\n\n**Params:** multipart/form-data: file= (pdf/png/jpg/gif/webp/tif/msg/eml/doc/docx/xls/xlsx), max 25MB\n\n**Example:**\n```\ncurl -X POST -H 'Authorization: Bearer ' -F file=@invoice.pdf http://localhost:5001/api/warranty/12/proof\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "warrantyid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "multipart/form-data: file= (pdf/png/jpg/gif/webp/tif/msg/eml/doc/docx/xls/xlsx), max 25MB" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-warranty" - ], - "summary": "Remove a proof document and clear both columns", - "description": "Remove a proof document and clear both columns\n\n**Auth:** jwt + warranty.edit\n\n**Params:** none\n\n**Example:**\n```\ncurl -X DELETE -H 'Authorization: Bearer ' http://localhost:5001/api/warranty/12/proof\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "warrantyid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/warranty/proof/{filename}": { - "get": { - "tags": [ - "plugin-warranty" - ], - "summary": "Download a proof document under the vendor's original filename. Authenticated: carries pricing and a service tag", - "description": "Download a proof document under the vendor's original filename. Authenticated: carries pricing and a service tag\n\n**Auth:** jwt + warranty.view\n\n**Params:** none\n\n**Example:**\n```\ncurl -OJ -H 'Authorization: Bearer ' http://localhost:5001/api/warranty/proof/warranty-12.pdf\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "filename", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, "/api/warranty/dashboard/expiring": { "get": { "tags": [ "plugin-warranty" ], - "summary": "Dashboard card: warranties running out and ones that already have, identified the way the floor identifies them - by...", - "description": "Dashboard card: warranties running out and ones that already have, identified the way the floor identifies them - by the PC's hostname and the machine it drives\n\n**Auth:** permission:warranty.view\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/warranty/dashboard/expiring\n```", + "summary": "Warranties running out, and ones that already have.", + "description": "Warranties running out, and ones that already have.\n\n**Auth:** permission:warranty.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/warranty/dashboard/expiring -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -19902,4202 +27958,13 @@ } } }, - "/api/backups/kinds": { + "/api/warranty/proof/{filename}": { "get": { "tags": [ - "plugin-backups" + "plugin-warranty" ], - "summary": "Registered backup kinds (NTLARS/DNC, part marker, UDC, CMM ...) with each kind's storage backend and download formats...", - "description": "Registered backup kinds (NTLARS/DNC, part marker, UDC, CMM ...) with each kind's storage backend and download formats, for UI that enumerates them rather than hardcoding the list\n\n**Auth:** permission:backups.view\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/backups/kinds\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/backups/asset/{assetid}": { - "get": { - "tags": [ - "plugin-backups" - ], - "summary": "Revision history for one asset, newest first. An unchanged configuration writes no revision, so the newest row may be...", - "description": "Revision history for one asset, newest first. An unchanged configuration writes no revision, so the newest row may be old and still current - each row carries both when it was captured and when it was last confirmed\n\n**Auth:** permission:backups.view\n\n**Params:** assetid in path; optional ?kind= to filter to one backup kind\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/backups/asset/42\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "assetid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/backups/asset/{assetid}/info": { - "get": { - "tags": [ - "plugin-backups" - ], - "summary": "A kind's at-a-glance card built from its latest revision, for the asset detail page", - "description": "A kind's at-a-glance card built from its latest revision, for the asset detail page\n\n**Auth:** permission:backups.view\n\n**Params:** assetid in path; ?kind= selects the kind whose card is wanted\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" \"http://localhost:5001/api/backups/asset/42/info?kind=ntlars\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "assetid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/backups/revisions/{backuprevisionid}": { - "get": { - "tags": [ - "plugin-backups" - ], - "summary": "One revision, including its parsed payload where the kind stores one (opaque kinds return metadata only)", - "description": "One revision, including its parsed payload where the kind stores one (opaque kinds return metadata only)\n\n**Auth:** permission:backups.view\n\n**Params:** backuprevisionid in path\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/backups/revisions/1188\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "backuprevisionid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/backups/revisions/{backuprevisionid}/diff": { - "get": { - "tags": [ - "plugin-backups" - ], - "summary": "Diff two revisions of the same asset and kind, key by key, which is what answers \"what changed on this machine\"", - "description": "Diff two revisions of the same asset and kind, key by key, which is what answers \"what changed on this machine\"\n\n**Auth:** permission:backups.view\n\n**Params:** backuprevisionid in path; ?against= to compare a specific pair, otherwise the previous revision of the same asset and kind\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/backups/revisions/1188/diff\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "backuprevisionid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/backups/revisions/{backuprevisionid}/download": { - "get": { - "tags": [ - "plugin-backups" - ], - "summary": "Render a revision back to its native file format - for NTLARS, a .reg in either registry dialect, so it can be loaded...", - "description": "Render a revision back to its native file format - for NTLARS, a .reg in either registry dialect, so it can be loaded straight back onto a controller PC. Returns the file, NOT the success envelope\n\n**Auth:** permission:backups.download\n\n**Params:** backuprevisionid in path; ?format= one of the kind's formats (see /api/backups/kinds)\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" -OJ \"http://localhost:5001/api/backups/revisions/1188/download?format=ntlars\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "backuprevisionid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/backups/dashboard/stale": { - "get": { - "tags": [ - "plugin-backups" - ], - "summary": "Dashboard card (contract 0.19.0): chains whose backup has stopped running, judged on when a config was last CONFIRMED...", - "description": "Dashboard card (contract 0.19.0): chains whose backup has stopped running, judged on when a config was last CONFIRMED rather than last changed\n\n**Auth:** permission:backups.view\n\n**Params:** none; the threshold is the backups_staledays setting, 0 disables the card\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/backups/dashboard/stale\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/tools/secrets": { - "post": { - "tags": [ - "plugin-tools" - ], - "summary": "Store an already-encrypted secret and return its one-view link token. The AES key lives in the URL fragment and never...", - "description": "Store an already-encrypted secret and return its one-view link token. The AES key lives in the URL fragment and never reaches the server, so what is held here is ciphertext nobody on this side can read - which is why SIZE is the only limit the server can enforce, and why this is for passwords and short text rather than documents\n\n**Auth:** permission:tools.secrets\n\n**Params:** body: ciphertext (base64, max 4096 chars), iv, expiryhours (1-168, default 24), views (1-5, default 1)\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" -d '{\"ciphertext\":\"...\",\"iv\":\"...\"}' http://localhost:5001/api/tools/secrets\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: ciphertext (base64, max 4096 chars), iv, expiryhours (1-168, default 24), views (1-5, default 1)" - } - } - } - } - } - }, - "/api/tools/secrets/{token}": { - "post": { - "tags": [ - "plugin-tools" - ], - "summary": "Redeem a secret: returns the ciphertext once and spends a view, deleting the row when the last view goes. POST rather...", - "description": "Redeem a secret: returns the ciphertext once and spends a view, deleting the row when the last view goes. POST rather than GET because chat and mail clients prefetch GET urls to build previews, and a preview must not consume a one-view secret. Missing, expired and spent answer identically so a guessed token learns nothing\n\n**Auth:** public\n\n**Params:** none\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/tools/secrets/9f8e7d6c5b4a39281706f5e4d3c2b1a0\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "token", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "none" - } - } - } - } - }, - "get": { - "tags": [ - "plugin-tools" - ], - "summary": "Whether a link is still live, WITHOUT spending it. Lets the read page say \"a secret is waiting and opening it will...", - "description": "Whether a link is still live, WITHOUT spending it. Lets the read page say \"a secret is waiting and opening it will destroy it\" instead of consuming the only view to find out, and gives link scanners something harmless to fetch\n\n**Auth:** public\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/tools/secrets/9f8e7d6c5b4a39281706f5e4d3c2b1a0\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "token", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/docs": { - "get": { - "tags": [ - "core-docs" - ], - "summary": "Redoc page rendering the generated spec. Public, and asset URLs are built with url_for so it works under any mount path", - "description": "Redoc page rendering the generated spec. Public, and asset URLs are built with url_for so it works under any mount path\n\n**Auth:** none\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/docs/\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/docs/openapi.json": { - "get": { - "tags": [ - "core-docs" - ], - "summary": "The generated OpenAPI 3.1 spec - the machine-readable description of this API, and what MCP and other clients build...", - "description": "The generated OpenAPI 3.1 spec - the machine-readable description of this API, and what MCP and other clients build from. Returns the spec, NOT the success envelope\n\n**Auth:** none\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/docs/openapi.json\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/docs/llms.txt": { - "get": { - "tags": [ - "core-docs" - ], - "summary": "Concise orientation guide for an LLM or a new integrator: auth schemes, the response envelope, and the common recipes...", - "description": "Concise orientation guide for an LLM or a new integrator: auth schemes, the response envelope, and the common recipes. Returns text/plain\n\n**Auth:** none\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/docs/llms.txt\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/docs/redoc.standalone.js": { - "get": { - "tags": [ - "core-docs" - ], - "summary": "Vendored Redoc bundle, served locally so the documentation page works on an air-gapped server", - "description": "Vendored Redoc bundle, served locally so the documentation page works on an air-gapped server\n\n**Auth:** none\n\n**Params:** none\n\n**Example:**\n```\ncurl -I http://localhost:5001/api/docs/redoc.standalone.js\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - } - }, - "/api/maplevels": { - "get": { - "tags": [ - "core-map" - ], - "summary": "Every building with its levels in display order, each carrying blueprint paths, native pixel size and marker count...", - "description": "Every building with its levels in display order, each carrying blueprint paths, native pixel size and marker count, plus defaultlevelid. PUBLIC: the printer installer map draws a blueprint before anyone logs in\n\n**Auth:** none\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/maplevels\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - } - } - }, - "post": { - "tags": [ - "core-map" - ], - "summary": "Create a level. Name and sort order are separate because levels are not reliably numbered (basement, mezzanine, roof)...", - "description": "Create a level. Name and sort order are separate because levels are not reliably numbered (basement, mezzanine, roof), and gaps let one be inserted later without renumbering\n\n**Auth:** jwt + role:admin\n\n**Params:** body: buildingid and levelname (required), sortorder, blueprintlight, blueprintdark, mapwidth, mapheight, isdefault\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" -X POST http://localhost:5001/api/maplevels\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: buildingid and levelname (required), sortorder, blueprintlight, blueprintdark, mapwidth, mapheight, isdefault" - } - } - } - } - } - }, - "/api/maplevels/{levelid}": { - "get": { - "tags": [ - "core-map" - ], - "summary": "One level: name, building, blueprints and native size. That size is what mapx/mapy on this level are pixels of (ADR-017)", - "description": "One level: name, building, blueprints and native size. That size is what mapx/mapy on this level are pixels of (ADR-017)\n\n**Auth:** none\n\n**Params:** levelid in path\n\n**Example:**\n```\ncurl http://localhost:5001/api/maplevels/2\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "levelid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "core-map" - ], - "summary": "Update a level. Changing mapwidth/mapheight returns a warning naming what it affects: the dimensions are the coordinate...", - "description": "Update a level. Changing mapwidth/mapheight returns a warning naming what it affects: the dimensions are the coordinate space every marker is expressed in, so a resize moves them all relative to the drawing - use a landmark transform instead\n\n**Auth:** jwt + role:admin\n\n**Params:** levelid in path; body: levelname, sortorder, blueprintlight, blueprintdark, mapwidth, mapheight, isdefault, isactive\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" -X PATCH http://localhost:5001/api/maplevels/1\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "levelid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "levelid in path; body: levelname, sortorder, blueprintlight, blueprintdark, mapwidth, mapheight, isdefault, isactive" - } - } - } - } - }, - "patch": { - "tags": [ - "core-map" - ], - "summary": "Update a level. Changing mapwidth/mapheight returns a warning naming what it affects: the dimensions are the coordinate...", - "description": "Update a level. Changing mapwidth/mapheight returns a warning naming what it affects: the dimensions are the coordinate space every marker is expressed in, so a resize moves them all relative to the drawing - use a landmark transform instead\n\n**Auth:** jwt + role:admin\n\n**Params:** levelid in path; body: levelname, sortorder, blueprintlight, blueprintdark, mapwidth, mapheight, isdefault, isactive\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" -X PATCH http://localhost:5001/api/maplevels/1\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "levelid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": false, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "levelid in path; body: levelname, sortorder, blueprintlight, blueprintdark, mapwidth, mapheight, isdefault, isactive" - } - } - } - } - }, - "delete": { - "tags": [ - "core-map" - ], - "summary": "Deactivate a level. Refused with 409 while assets are placed on it, and refused for the default level: deleting a...", - "description": "Deactivate a level. Refused with 409 while assets are placed on it, and refused for the default level: deleting a drawing out from under a marker leaves a position in a coordinate space that no longer exists\n\n**Auth:** jwt + role:admin\n\n**Params:** levelid in path\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" -X DELETE http://localhost:5001/api/maplevels/3\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "levelid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/maplevels/{levelid}/blueprint/{filename}": { - "get": { - "tags": [ - "core-map" - ], - "summary": "Serve a level's blueprint image, with sandbox headers so an SVG floor plan cannot execute as script", - "description": "Serve a level's blueprint image, with sandbox headers so an SVG floor plan cannot execute as script\n\n**Auth:** none\n\n**Params:** levelid and filename in path\n\n**Example:**\n```\ncurl -I http://localhost:5001/api/maplevels/2/blueprint/level-2-light.png\n```", - "security": [], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "levelid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "filename", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/maplevels/buildings": { - "post": { - "tags": [ - "core-map" - ], - "summary": "Create a building. An asset references the level, never the building, so the two cannot disagree", - "description": "Create a building. An asset references the level, never the building, so the two cannot disagree\n\n**Auth:** jwt + role:admin\n\n**Params:** body: buildingname (required), sortorder\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"buildingname\":\"Annex\"}' -X POST http://localhost:5001/api/maplevels/buildings\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: buildingname (required), sortorder" - } - } - } - } - } - }, - "/api/maplevels/buildings/{buildingid}": { - "put": { - "tags": [ - "core-map" - ], - "summary": "Rename or reorder a building. Levels move with it and nothing repositions", - "description": "Rename or reorder a building. Levels move with it and nothing repositions\n\n**Auth:** jwt + role:admin\n\n**Params:** buildingid in path; body: buildingname, sortorder, isactive\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" -X PATCH http://localhost:5001/api/maplevels/buildings/1\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "buildingid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "buildingid in path; body: buildingname, sortorder, isactive" - } - } - } - } - }, - "patch": { - "tags": [ - "core-map" - ], - "summary": "Rename or reorder a building. Levels move with it and nothing repositions", - "description": "Rename or reorder a building. Levels move with it and nothing repositions\n\n**Auth:** jwt + role:admin\n\n**Params:** buildingid in path; body: buildingname, sortorder, isactive\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" -X PATCH http://localhost:5001/api/maplevels/buildings/1\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "buildingid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": false, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "buildingid in path; body: buildingname, sortorder, isactive" - } - } - } - } - } - }, - "/api/maplevels/{levelid}/blueprint": { - "post": { - "tags": [ - "core-map" - ], - "summary": "Upload a level's blueprint. Reads the image's real pixel size from its header and adopts it when the level is EMPTY...", - "description": "Upload a level's blueprint. Reads the image's real pixel size from its header and adopts it when the level is EMPTY; with markers already placed it reports the mismatch and changes nothing, because adopting a new coordinate space silently moves every marker while looking like a successful upload\n\n**Auth:** jwt + role:admin\n\n**Params:** levelid in path; multipart/form-data: file=, theme=light|dark\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" -F file=@second-floor.png -F theme=light -X POST http://localhost:5001/api/maplevels/2/blueprint\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "levelid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "levelid in path; multipart/form-data: file=, theme=light|dark" - } - } - } - } - } - }, - "/api/mappositions/transform": { - "post": { - "tags": [ - "core-map" - ], - "summary": "Move every placed marker on a level by a transform derived per axis from landmark pairs. NEVER from image dimensions: a...", - "description": "Move every placed marker on a level by a transform derived per axis from landmark pairs. NEVER from image dimensions: a level added below another changes canvas height without rescaling anything, and a dimension-derived scale would stretch Y and be wrong everywhere. Dry run returns every old and new position plus which land outside the target. Applying snapshots first and clears mapverifiedat, because a transformed position is a guess awaiting review\n\n**Auth:** permission:assets.edit\n\n**Params:** body: levelid (required), landmarks [{fromx,fromy,tox,toy}] (two or more), tolevelid, assetids, dryrun (defaults TRUE)\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"levelid\":1,\"landmarks\":[{\"fromx\":0,\"fromy\":0,\"tox\":0,\"toy\":1450},{\"fromx\":1000,\"fromy\":1000,\"tox\":1000,\"toy\":2450}]}' -X POST http://localhost:5001/api/mappositions/transform\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: levelid (required), landmarks [{fromx,fromy,tox,toy}] (two or more), tolevelid, assetids, dryrun (defaults TRUE)" - } - } - } - } - } - }, - "/api/mappositions/positions": { - "post": { - "tags": [ - "core-map" - ], - "summary": "Set many positions at once, snapshotting first. levelid is per position rather than per request because a bulk save can...", - "description": "Set many positions at once, snapshotting first. levelid is per position rather than per request because a bulk save can span levels and inferring it is the guess this model exists to remove. Placing by hand counts as review, so mapverifiedat is stamped\n\n**Auth:** permission:assets.edit\n\n**Params:** body: positions [{assetid, mapx, mapy, levelid}] - levelid required per row - and verified\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"positions\":[{\"assetid\":42,\"mapx\":1200,\"mapy\":900,\"levelid\":2}]}' -X POST http://localhost:5001/api/mappositions/positions\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: positions [{assetid, mapx, mapy, levelid}] - levelid required per row - and verified" - } - } - } - } - } - }, - "/api/mappositions/verify": { - "post": { - "tags": [ - "core-map" - ], - "summary": "Mark markers reviewed against the current drawing without moving them - the common case in a review pass. No snapshot...", - "description": "Mark markers reviewed against the current drawing without moving them - the common case in a review pass. No snapshot, because no position changes\n\n**Auth:** permission:assets.edit\n\n**Params:** body: assetids (required), unverify\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"assetids\":[42,43]}' -X POST http://localhost:5001/api/mappositions/verify\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: assetids (required), unverify" - } - } - } - } - } - }, - "/api/mappositions/snapshots": { - "get": { - "tags": [ - "core-map" - ], - "summary": "Position snapshots, newest first, with what caused each and whether it has been restored. Metadata only - the positions...", - "description": "Position snapshots, newest first, with what caused each and whether it has been restored. Metadata only - the positions are large\n\n**Auth:** permission:assets.view\n\n**Params:** none; newest 50\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/mappositions/snapshots\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/mappositions/snapshots/{snapshotid}/restore": { - "post": { - "tags": [ - "core-map" - ], - "summary": "Put a snapshot back, snapshotting first so an undo is itself undoable. Restores level and review state, not just...", - "description": "Put a snapshot back, snapshotting first so an undo is itself undoable. Restores level and review state, not just coordinates, and reports assets that no longer exist rather than failing the whole restore\n\n**Auth:** permission:assets.edit\n\n**Params:** snapshotid in path\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" -X POST http://localhost:5001/api/mappositions/snapshots/7/restore\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "snapshotid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "snapshotid in path" - } - } - } - } - } - }, - "/api/guides": { - "get": { - "tags": [ - "guides" - ], - "summary": "List guides. Published guides plus the caller's own drafts; someone with guides.edit sees every draft", - "description": "List guides. Published guides plus the caller's own drafts; someone with guides.edit sees every draft\n\n**Auth:** jwt + guides.view\n\n**Params:** query: q (searches title, summary, tags and body), tag\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/guides?tag=partmarker' -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - }, - "post": { - "tags": [ - "guides" - ], - "summary": "Create a guide as a draft. Every block needs a unique, stable id: the id is what a reader's tick is remembered against...", - "description": "Create a guide as a draft. Every block needs a unique, stable id: the id is what a reader's tick is remembered against, and a save without one is refused\n\n**Auth:** jwt + guides.create\n\n**Params:** body: title (required), summary, tags (comma separated), blocks (array), note\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/guides -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"title\":\"Swap a controller\",\"tags\":\"partmarker\",\"blocks\":[{\"id\":\"b1\",\"type\":\"paragraph\",\"text\":\"Power it off.\"}]}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: title (required), summary, tags (comma separated), blocks (array), note" - } - } - } - } - } - }, - "/api/guides/tags": { - "get": { - "tags": [ - "guides" - ], - "summary": "Every tag in use with a count, for filter chips", - "description": "Every tag in use with a count, for filter chips\n\n**Auth:** jwt + guides.view\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/guides/tags -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/guides/{slug}": { - "get": { - "tags": [ - "guides" - ], - "summary": "Read a guide as a READER: returns the PUBLISHED revision, not the working copy, so an edit in progress does not change...", - "description": "Read a guide as a READER: returns the PUBLISHED revision, not the working copy, so an edit in progress does not change what somebody following the procedure sees. 404 (not 403) for an unpublished guide the caller cannot edit\n\n**Auth:** jwt + guides.view\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/guides/replace-a-marker -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "slug", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/guides/{guideid}/working": { - "get": { - "tags": [ - "guides" - ], - "summary": "The working copy, for the editor. Requires guides.edit, or guides.create while the guide is your own and unpublished", - "description": "The working copy, for the editor. Requires guides.edit, or guides.create while the guide is your own and unpublished\n\n**Auth:** jwt + guides.view (+ edit rights)\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/guides/12/working -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "guideid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/guides/{guideid}/revisions": { - "get": { - "tags": [ - "guides" - ], - "summary": "Version history, newest first, flagging which revision is published", - "description": "Version history, newest first, flagging which revision is published\n\n**Auth:** jwt + guides.view\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/guides/12/revisions -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "guideid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/guides/revisions/{revisionid}": { - "get": { - "tags": [ - "guides" - ], - "summary": "One revision including its blocks, for viewing or comparing an old version", - "description": "One revision including its blocks, for viewing or comparing an old version\n\n**Auth:** jwt + guides.view\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/guides/revisions/34 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "revisionid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/guides/{guideid}": { - "put": { - "tags": [ - "guides" - ], - "summary": "Save the working copy. Writes a new revision ONLY when the content hash changes, so an autosave that changed nothing...", - "description": "Save the working copy. Writes a new revision ONLY when the content hash changes, so an autosave that changed nothing records nothing; the response carries saved=false in that case\n\n**Auth:** jwt + guides.view (+ edit rights)\n\n**Params:** body: any of title, summary, tags, blocks, note\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/guides/12 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"blocks\":[]}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "guideid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: any of title, summary, tags, blocks, note" - } - } - } - } - }, - "delete": { - "tags": [ - "guides" - ], - "summary": "Delete a guide and its revisions and images", - "description": "Delete a guide and its revisions and images\n\n**Auth:** jwt + guides.delete\n\n**Params:** none\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/guides/12 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "guideid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/guides/{guideid}/publish": { - "post": { - "tags": [ - "guides" - ], - "summary": "Point readers at the latest saved revision. Promotes an existing revision rather than snapshotting, so a publish cannot...", - "description": "Point readers at the latest saved revision. Promotes an existing revision rather than snapshotting, so a publish cannot ship unsaved edits nobody reviewed\n\n**Auth:** jwt + guides.publish\n\n**Params:** none\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/guides/12/publish -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "guideid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "none" - } - } - } - } - } - }, - "/api/guides/{guideid}/unpublish": { - "post": { - "tags": [ - "guides" - ], - "summary": "Withdraw from readers without deleting anything; revisions survive so the guide can be corrected and published again", - "description": "Withdraw from readers without deleting anything; revisions survive so the guide can be corrected and published again\n\n**Auth:** jwt + guides.publish\n\n**Params:** none\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/guides/12/unpublish -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "guideid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "none" - } - } - } - } - } - }, - "/api/guides/{guideid}/restore/{revisionid}": { - "post": { - "tags": [ - "guides" - ], - "summary": "Bring an old revision back as a NEW revision. History is never rewritten, so what was published in between stays on...", - "description": "Bring an old revision back as a NEW revision. History is never rewritten, so what was published in between stays on record\n\n**Auth:** jwt + guides.view (+ edit rights)\n\n**Params:** none\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/guides/12/restore/34 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "guideid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "revisionid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "none" - } - } - } - } - } - }, - "/api/guides/images": { - "post": { - "tags": [ - "guides" - ], - "summary": "Upload an image for a guide. guideid is optional because an image is pasted before a new guide has an id; the editor...", - "description": "Upload an image for a guide. guideid is optional because an image is pasted before a new guide has an id; the editor attaches it on save\n\n**Auth:** jwt + guides.create\n\n**Params:** multipart: file (png/jpeg/gif/webp, max 8 MB), guideid (optional)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/guides/images -H \"Authorization: Bearer $TOK\" -F file=@screenshot.png -F guideid=12\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "multipart: file (png/jpeg/gif/webp, max 8 MB), guideid (optional)" - } - } - } - } - } - }, - "/api/guides/images/{imageid}": { - "get": { - "tags": [ - "guides" - ], - "summary": "Serve an uploaded image; cached immutably since an image is replaced rather than edited", - "description": "Serve an uploaded image; cached immutably since an image is replaced rather than edited\n\n**Auth:** jwt + guides.view\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/guides/images/5 -H \"Authorization: Bearer $TOK\" --output shot.png\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "imageid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "List endpoint states. Registered both with and without the trailing slash, so both forms are described here", - "description": "List endpoint states. Registered both with and without the trailing slash, so both forms are described here\n\n**Auth:** endpoints.view\n\n**Params:** query: search (hostname substring)\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/endpoints/{asset_id}": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "One endpoint: its agent state, version, and last check-in", - "description": "One endpoint: its agent state, version, and last check-in\n\n**Auth:** endpoints.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/1asset_id\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/{asset_id}/events": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Agent event history for one endpoint, newest first", - "description": "Agent event history for one endpoint, newest first\n\n**Auth:** endpoints.view\n\n**Params:** path: asset_id; query: eventid (page from this id)\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/1asset_id/events\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/asset/{asset_id}/status": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Asset-detail panel: online state, uptime, and agent version for one endpoint", - "description": "Asset-detail panel: online state, uptime, and agent version for one endpoint\n\n**Auth:** endpoints.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1asset_id/status\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/asset/{asset_id}/telemetrymeta": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Asset-detail panel: what telemetry this endpoint reports and when each series was last updated", - "description": "Asset-detail panel: what telemetry this endpoint reports and when each series was last updated\n\n**Auth:** endpoints.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1asset_id/telemetrymeta\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/asset/{asset_id}/services": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Asset-detail panel: Windows services the agent watches on this endpoint, with running state", - "description": "Asset-detail panel: Windows services the agent watches on this endpoint, with running state\n\n**Auth:** endpoints.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1asset_id/services\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/asset/{asset_id}/recentevents": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Asset-detail panel: the most recent agent events for one endpoint", - "description": "Asset-detail panel: the most recent agent events for one endpoint\n\n**Auth:** endpoints.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1asset_id/recentevents\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/asset/{asset_id}/activity": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Asset-detail panel: one chronological feed merging curated Windows events with operator commands, newest first, capped...", - "description": "Asset-detail panel: one chronological feed merging curated Windows events with operator commands, newest first, capped at 15. Both are keyed by assetid, so this is two indexed reads and a merge rather than a join. A failed command is marked critical. The requester name is withheld from callers who do not themselves hold endpoints.command, so a read-only viewer gets the history without the roster of who restarted what\n\n**Auth:** endpoints.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/activity\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/asset/{asset_id}/interfaces": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Asset-detail panel: network interfaces as the collector reports them ({alias, ipv4, mac, gateway, subnet, dhcp})", - "description": "Asset-detail panel: network interfaces as the collector reports them ({alias, ipv4, mac, gateway, subnet, dhcp})\n\n**Auth:** endpoints.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1asset_id/interfaces\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/dashboard/summary": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Dashboard card: endpoint counts by online/offline/never-seen", - "description": "Dashboard card: endpoint counts by online/offline/never-seen\n\n**Auth:** endpoints.view\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/dashboard/summary\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/endpoints/dashboard/quiet": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Dashboard card: endpoints that have gone quiet - no check-in inside the configured quiet-hours window", - "description": "Dashboard card: endpoints that have gone quiet - no check-in inside the configured quiet-hours window\n\n**Auth:** endpoints.view\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/dashboard/quiet\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/endpoints/dashboard/stability": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Dashboard card: endpoints ranked by reboot/agent-restart churn, to surface a machine that is flapping", - "description": "Dashboard card: endpoints ranked by reboot/agent-restart churn, to surface a machine that is flapping\n\n**Auth:** endpoints.view\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/dashboard/stability\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/endpoints/agentkey": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Status of the endpoints agent key: whether one exists, when it was issued, and its last use. Never returns the key...", - "description": "Status of the endpoints agent key: whether one exists, when it was issued, and its last use. Never returns the key itself\n\n**Auth:** admin role\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/agentkey\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - }, - "delete": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Revoke the endpoints agent key. The kill switch for agent ingest: reports and command polls stop being authorized...", - "description": "Revoke the endpoints agent key. The kill switch for agent ingest: reports and command polls stop being authorized immediately\n\n**Auth:** admin role\n\n**Params:** none\n\n**Example:**\n```\ncurl -X DELETE -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/agentkey\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/endpoints/agentkey/regenerate": { - "post": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Issue a new endpoints agent key and return it ONCE. The key is scoped to collector.ingest.endpoints only, so rotating...", - "description": "Issue a new endpoints agent key and return it ONCE. The key is scoped to collector.ingest.endpoints only, so rotating it cannot disturb the computers or GE-Enforce collectors. Every agent must be reconfigured with the new value\n\n**Auth:** admin role\n\n**Params:** none\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/agentkey/regenerate\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "none" - } - } - } - } - } - }, - "/api/endpoints/{asset_id}/commands": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Command history for one endpoint: what was queued, claimed, and what it returned", - "description": "Command history for one endpoint: what was queued, claimed, and what it returned\n\n**Auth:** endpoints.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/1asset_id/commands\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "post": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Queue one allowlisted command for an endpoint. Unknown actions are rejected, destructive ones (system_restart) demand a...", - "description": "Queue one allowlisted command for an endpoint. Unknown actions are rejected, destructive ones (system_restart) demand a stricter permission, the effective policy must permit the action for this endpoint, and a second command will not stack while one is pending or running\n\n**Auth:** endpoints.command\n\n**Params:** path: asset_id; body: action (required, allowlisted), target, targetkind\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"action\":\"service_restart\",\"target\":\"MTConnectAgent\",\"targetkind\":\"service\"}' http://localhost:5001/api/endpoints/1/commands\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: asset_id; body: action (required, allowlisted), target, targetkind" - } - } - } - } - } - }, - "/api/endpoints/commands/next": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Agent long-poll. Claims the oldest pending command for the calling host and atomically marks it running so no second...", - "description": "Agent long-poll. Claims the oldest pending command for the calling host and atomically marks it running so no second poll claims it; 204 when nothing is queued inside the hold window, and the agent re-polls. Collector-token auth, not JWT\n\n**Auth:** collector token (collector.ingest.endpoints)\n\n**Params:** query: hostname (required)\n\n**Example:**\n```\ncurl -H \"X-Collector-Key: $KEY\" \"http://localhost:5001/api/endpoints/commands/next?hostname=WJSF-PC-01\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/endpoints/commands/{command_id}/result": { - "post": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Agent reports a claimed command result. Finalizes the command row and, for MTConnect actions that observed run state...", - "description": "Agent reports a claimed command result. Finalizes the command row and, for MTConnect actions that observed run state, caches that on the EndpointState so the UI toggle reflects reality between polls - both in one commit. Collector-token auth, not JWT\n\n**Auth:** collector token (collector.ingest.endpoints)\n\n**Params:** path: command_id; body: the command outcome (status, output)\n\n**Example:**\n```\ncurl -X POST -H \"X-Collector-Key: $KEY\" -H 'Content-Type: application/json' -d '{\"status\":\"succeeded\",\"output\":\"ok\"}' http://localhost:5001/api/endpoints/commands/1/result\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "command_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: command_id; body: the command outcome (status, output)" - } - } - } - } - } - }, - "/api/endpoints/monitors": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "List every monitor definition, shipped and local, ordered shipped-first then by display name", - "description": "List every monitor definition, shipped and local, ordered shipped-first then by display name\n\n**Auth:** jwt + require_permission endpoints.view\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/monitors -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - }, - "post": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Create a local monitor definition; source is forced to 'local' so a caller cannot mint a shipped row", - "description": "Create a local monitor definition; source is forced to 'local' so a caller cannot mint a shipped row\n\n**Auth:** jwt + require_role admin\n\n**Params:** body: kind (process|service, required), displayname (required), processname (required for process), servicename (required for service), launchpath, datacollectorpath, logcollectorpath, isactive (default true)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/monitors -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"kind\":\"service\",\"displayname\":\"Spooler\",\"servicename\":\"Spooler\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: kind (process|service, required), displayname (required), processname (required for process), servicename (required for service), launchpath, datacollectorpath, logcollectorpath, isactive (default true)" - } - } - } - } - } - }, - "/api/endpoints/monitors/{monitor_id}": { - "put": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Update a local monitor definition; 403 on a shipped row, which is product-owned and read-only", - "description": "Update a local monitor definition; 403 on a shipped row, which is product-owned and read-only\n\n**Auth:** jwt + require_role admin\n\n**Params:** path: monitor_id; body: same fields as create, all optional\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/endpoints/monitors/4 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"isactive\":false}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "monitor_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: monitor_id; body: same fields as create, all optional" - } - } - } - } - }, - "delete": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Delete a local monitor definition; 403 on a shipped row", - "description": "Delete a local monitor definition; 403 on a shipped row\n\n**Auth:** jwt + require_role admin\n\n**Params:** path: monitor_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/endpoints/monitors/4 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "monitor_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/monitors/resolve": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Resolve the monitor set a PC type should watch: shipped defaults for 'Shopfloor PC' plus whatever active site policy...", - "description": "Resolve the monitor set a PC type should watch: shipped defaults for 'Shopfloor PC' plus whatever active site policy attaches to that type. The human-facing view of what the agent will be told\n\n**Auth:** jwt + require_permission endpoints.view\n\n**Params:** query: pctype (required)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/endpoints/monitors/resolve?pctype=Shopfloor+PC' -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/endpoints/monitors/resolved": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Agent runtime watch-list. The agent calls this each telemetry cycle and receives {processes[], services[]} shaped like...", - "description": "Agent runtime watch-list. The agent calls this each telemetry cycle and receives {processes[], services[]} shaped like its own MonitorConfig, so a policy-attached monitor is watched without editing appsettings.json on the bay. Unknown hostname is a 404; a PC type with no monitors returns empty lists\n\n**Auth:** endpoints collector API key (X-API-Key/Bearer), not JWT\n\n**Params:** query: hostname (required)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/endpoints/monitors/resolved?hostname=F7MHPF24' -H \"X-API-Key: $ENDPOINTS_KEY\"\n```", - "security": [ - { - "apiKeyAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/endpoints/operatorreports": { - "post": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Agent uploads one operator issue report captured from the tray. The hostname must resolve to a computer asset. The...", - "description": "Agent uploads one operator issue report captured from the tray. The hostname must resolve to a computer asset. The screenshot is stored under a server-generated uuid filename; a retention sweep runs opportunistically so the feed stays bounded without a scheduler\n\n**Auth:** endpoints collector API key (X-API-Key/Bearer), not JWT\n\n**Params:** multipart/form-data: hostname (required), reason (default 'Other (free text)', truncated to 128), details, screenshot (png, 10 MB cap)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/operatorreports -H \"X-API-Key: $ENDPOINTS_KEY\" -F hostname=F7MHPF24 -F 'details=Machine will not home' -F screenshot=@shot.png\n```", - "security": [ - { - "apiKeyAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "multipart/form-data: hostname (required), reason (default 'Other (free text)', truncated to 128), details, screenshot (png, 10 MB cap)" - } - } - } - } - } - }, - "/api/endpoints/panel/{asset_id}/operatorreports": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Operator Reports asset panel: this endpoint's reports newest first, each carrying a hasimage flag so the viewer knows...", - "description": "Operator Reports asset panel: this endpoint's reports newest first, each carrying a hasimage flag so the viewer knows whether to request the screenshot\n\n**Auth:** jwt + require_permission endpoints.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/panel/46/operatorreports -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/operatorreports/{operatorreportid}/image": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Serve one operator report's screenshot, hardened with the shared upload headers (CSP sandbox + nosniff); 404 when the...", - "description": "Serve one operator report's screenshot, hardened with the shared upload headers (CSP sandbox + nosniff); 404 when the report carries no image\n\n**Auth:** jwt + require_permission endpoints.view\n\n**Params:** path: operatorreportid\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/operatorreports/12/image -H \"Authorization: Bearer $TOK\" -o shot.png\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "operatorreportid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/asset/{asset_id}/hardware": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Asset-detail panel: processor, memory and chassis inventory. Agent 1.7.0 and later only; an older agent leaves the...", - "description": "Asset-detail panel: processor, memory and chassis inventory. Agent 1.7.0 and later only; an older agent leaves the columns null and the panel hides itself rather than rendering blanks\n\n**Auth:** endpoints.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/hardware\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/asset/{asset_id}/osinfo": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Asset-detail panel: operating system, firmware and domain membership as one card (both answer \"what is this box...", - "description": "Asset-detail panel: operating system, firmware and domain membership as one card (both answer \"what is this box running\")\n\n**Auth:** endpoints.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/osinfo\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/asset/{asset_id}/storage": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Asset-detail panel: volume capacity and physical drive health as two sections. They share no key (volumes vs physical...", - "description": "Asset-detail panel: volume capacity and physical drive health as two sections. They share no key (volumes vs physical devices) and are never reconciled across sections\n\n**Auth:** endpoints.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/storage\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/asset/{asset_id}/diskhealth": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Physical drive health rows, anything predicting failure sorted first. No longer backs a panel of its own (Storage...", - "description": "Physical drive health rows, anything predicting failure sorted first. No longer backs a panel of its own (Storage renders it as a section) but kept as a stable read for callers outside the panel renderer\n\n**Auth:** endpoints.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/diskhealth\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/asset/{asset_id}/listeners": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Asset-detail panel: TCP sockets in LISTEN with the owning process, sorted by port. The full connection table is an...", - "description": "Asset-detail panel: TCP sockets in LISTEN with the owning process, sorted by port. The full connection table is an on-demand command, not standard telemetry\n\n**Auth:** endpoints.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/listeners\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/asset/{asset_id}/mappeddrives": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Asset-detail panel: mapped network drives with a health badge each. Session-scoped, so an empty list when nobody is...", - "description": "Asset-detail panel: mapped network drives with a health badge each. Session-scoped, so an empty list when nobody is signed in is a valid state, not a failure\n\n**Auth:** endpoints.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/mappeddrives\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/asset/{asset_id}/usercontext": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Asset-detail panel: session-scoped data from the interactive user probe. Mapped drives arrive from two sources that can...", - "description": "Asset-detail panel: session-scoped data from the interactive user probe. Mapped drives arrive from two sources that can disagree and the worse status wins, so a half-broken share is never shown healthy\n\n**Auth:** endpoints.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/usercontext\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/asset/{asset_id}/iphistory": { - "get": { - "tags": [ - "plugin-endpoints" - ], - "summary": "Asset-detail panel: every address this asset has held, newest change first. \"Last changed\" is when a field on the row...", - "description": "Asset-detail panel: every address this asset has held, newest change first. \"Last changed\" is when a field on the row moved, NOT when the address was last seen\n\n**Auth:** endpoints.view\n\n**Params:** path: asset_id\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/iphistory\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/scripts": { - "get": { - "tags": [ - "core-scripts" - ], - "summary": "List script catalog entries, active only by default", - "description": "List script catalog entries, active only by default\n\n**Auth:** jwt + require_permission applications.view\n\n**Params:** query: includeinactive (default false)\n\n**Example:**\n```\ncurl http://localhost:5001/api/scripts -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - }, - "post": { - "tags": [ - "core-scripts" - ], - "summary": "Create a script catalog entry; the body is uploaded separately so metadata and content version independently", - "description": "Create a script catalog entry; the body is uploaded separately so metadata and content version independently\n\n**Auth:** jwt + require_permission applications.create\n\n**Params:** body: name (required), description, runcontext (system|user, default system)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/scripts -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"name\":\"Restart spooler\",\"runcontext\":\"system\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: name (required), description, runcontext (system|user, default system)" - } - } - } - } - } - }, - "/api/scripts/{script_id}": { - "get": { - "tags": [ - "core-scripts" - ], - "summary": "Get one script entry with its metadata and run context", - "description": "Get one script entry with its metadata and run context\n\n**Auth:** jwt + require_permission applications.view\n\n**Params:** path: script_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/scripts/3 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "script_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "put": { - "tags": [ - "core-scripts" - ], - "summary": "Update a script entry's metadata; the stored body is untouched", - "description": "Update a script entry's metadata; the stored body is untouched\n\n**Auth:** jwt + require_permission applications.edit\n\n**Params:** path: script_id; body: name, description, runcontext, isactive\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/scripts/3 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"description\":\"Clears the print queue\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "script_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: script_id; body: name, description, runcontext, isactive" - } - } - } - } - }, - "delete": { - "tags": [ - "core-scripts" - ], - "summary": "Remove a script catalog entry", - "description": "Remove a script catalog entry\n\n**Auth:** jwt + require_permission applications.delete\n\n**Params:** path: script_id\n\n**Example:**\n```\ncurl -X DELETE http://localhost:5001/api/scripts/3 -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "script_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/scripts/{script_id}/body": { - "post": { - "tags": [ - "core-scripts" - ], - "summary": "Upload or replace the script body; stored on disk and hashed so a delivery can pin the exact bytes", - "description": "Upload or replace the script body; stored on disk and hashed so a delivery can pin the exact bytes\n\n**Auth:** jwt + require_permission applications.edit\n\n**Params:** path: script_id; multipart/form-data: file (the .ps1)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/scripts/3/body -H \"Authorization: Bearer $TOK\" -F 'file=@restart-spooler.ps1'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "script_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: script_id; multipart/form-data: file (the .ps1)" - } - } - } - } - } - }, - "/api/scripts/body/{filename}": { - "get": { - "tags": [ - "core-scripts" - ], - "summary": "Download a stored script body for review in the UI", - "description": "Download a stored script body for review in the UI\n\n**Auth:** jwt + require_permission applications.view\n\n**Params:** path: filename (as stored)\n\n**Example:**\n```\ncurl http://localhost:5001/api/scripts/body/restart-spooler.ps1 -H \"Authorization: Bearer $TOK\" -o restart-spooler.ps1\n```", + "summary": "Download a proof document.", + "description": "Download a proof document.\n\n**Auth:** permission:warranty.view\n\n**Params:** path: filename (path)\n\n**Example:**\n```\ncurl http://localhost:5001/api/warranty/proof/example.png -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -24136,282 +28003,20 @@ "in": "path", "required": true, "schema": { - "type": "string" + "type": "string", + "description": "may contain slashes" } } ] } }, - "/api/endpoints/apppackage/{appversionid}": { + "/api/warranty/report": { "get": { "tags": [ - "endpoints-delivery" + "plugin-warranty" ], - "summary": "Agent-facing installer download for a pinned app version; serves the exact bytes the update_app descriptor pinned by...", - "description": "Agent-facing installer download for a pinned app version; serves the exact bytes the update_app descriptor pinned by SHA-256\n\n**Auth:** collector token (X-API-Key), endpoints scope\n\n**Params:** path: appversionid (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/apppackage/12 -H 'X-API-Key: $COLLECTOR_KEY' -o setup.exe\n```", - "security": [ - { - "apiKeyAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "appversionid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/apppackage/app/{app_id}": { - "get": { - "tags": [ - "endpoints-delivery" - ], - "summary": "Agent-facing installer download resolved from the application rather than a specific version", - "description": "Agent-facing installer download resolved from the application rather than a specific version\n\n**Auth:** collector token (X-API-Key), endpoints scope\n\n**Params:** path: app_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/apppackage/app/4 -H 'X-API-Key: $COLLECTOR_KEY' -o setup.exe\n```", - "security": [ - { - "apiKeyAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "app_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/apppackage/app/{app_id}/{filename}": { - "get": { - "tags": [ - "endpoints-delivery" - ], - "summary": "As above, naming the stored file explicitly so the agent writes it back under the expected name", - "description": "As above, naming the stored file explicitly so the agent writes it back under the expected name\n\n**Auth:** collector token (X-API-Key), endpoints scope\n\n**Params:** path: app_id (int), filename\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/apppackage/app/4/setup.exe -H 'X-API-Key: $COLLECTOR_KEY' -o setup.exe\n```", - "security": [ - { - "apiKeyAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "app_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "filename", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/scriptbody/{script_id}": { - "get": { - "tags": [ - "endpoints-delivery" - ], - "summary": "Agent-facing script download for a run_script delivery, on the same key as the rest of the delivery path", - "description": "Agent-facing script download for a run_script delivery, on the same key as the rest of the delivery path\n\n**Auth:** collector token (X-API-Key), endpoints scope\n\n**Params:** path: script_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/scriptbody/3 -H 'X-API-Key: $COLLECTOR_KEY' -o run.ps1\n```", - "security": [ - { - "apiKeyAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "script_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/asset/{asset_id}/applications": { - "get": { - "tags": [ - "endpoints-delivery" - ], - "summary": "Installed-software snapshot the agent last reported for this endpoint, for the asset detail panel; a per-report...", - "description": "Installed-software snapshot the agent last reported for this endpoint, for the asset detail panel; a per-report snapshot rather than history\n\n**Auth:** jwt + require_permission endpoints.view\n\n**Params:** path: asset_id (int)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/asset/313/applications -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "asset_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/endpoints/siteagent": { - "get": { - "tags": [ - "endpoints-siteagent" - ], - "summary": "Report whether a prebuilt agent binary is staged and can therefore be packaged", - "description": "Report whether a prebuilt agent binary is staged and can therefore be packaged\n\n**Auth:** jwt + require_role admin\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/siteagent -H \"Authorization: Bearer $TOK\"\n```", + "summary": "Counts + lists bucketed by derived status.", + "description": "Counts + lists bucketed by derived status.\n\n**Auth:** permission:warranty.view\n\n**Params:** not documented\n\n**Example:**\n```\ncurl http://localhost:5001/api/warranty/report -H \"Authorization: Bearer $TOK\"\n```", "security": [ { "bearerAuth": [] @@ -24443,13 +28048,13 @@ } } }, - "/api/endpoints/siteagent/package": { + "/api/warranty/sync/dell": { "post": { "tags": [ - "endpoints-siteagent" + "plugin-warranty" ], - "summary": "Bundle the agent as a downloadable ZIP with a generated install.ps1 that provisions credentials, so a bay install needs...", - "description": "Bundle the agent as a downloadable ZIP with a generated install.ps1 that provisions credentials, so a bay install needs no hand editing\n\n**Auth:** jwt + require_role admin\n\n**Params:** body: baseurl, collectorkey, cadenceseconds, and the site TLS cert when one is readable\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/siteagent/package -H \"Authorization: Bearer $TOK\" -o shopdb-agent.zip\n```", + "summary": "Look up every asset serial (= Dell service tag) that has no warranty yet and create warranties for the ones Dell...", + "description": "Look up every asset serial (= Dell service tag) that has no warranty yet and create warranties for the ones Dell recognizes.\n\n**Auth:** permission:warranty.edit\n\n**Params:** body: JSON (fields not yet documented)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/warranty/sync/dell -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'\n```", "security": [ { "bearerAuth": [] @@ -24486,770 +28091,7 @@ "schema": { "type": "object", "additionalProperties": true, - "description": "body: baseurl, collectorkey, cadenceseconds, and the site TLS cert when one is readable" - } - } - } - } - } - }, - "/api/endpoints/adoptiontool/package": { - "post": { - "tags": [ - "endpoints-siteagent" - ], - "summary": "Bundle the agentless adoption tool, for taking an inventory of a machine that will not run the service", - "description": "Bundle the agentless adoption tool, for taking an inventory of a machine that will not run the service\n\n**Auth:** jwt + require_role admin\n\n**Params:** body: as for the site agent package\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/adoptiontool/package -H \"Authorization: Bearer $TOK\" -o shopdb-adoption.zip\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: as for the site agent package" - } - } - } - } - } - }, - "/api/endpoints/siteagent/binaries/{binkey}": { - "get": { - "tags": [ - "endpoints-siteagent" - ], - "summary": "List the staged versions of one agent binary, newest first, with the current one marked", - "description": "List the staged versions of one agent binary, newest first, with the current one marked\n\n**Auth:** jwt + require_permission endpoints.agentpackage\n\n**Params:** path: binkey (agent|tray)\n\n**Example:**\n```\ncurl http://localhost:5001/api/endpoints/siteagent/binaries/agent -H \"Authorization: Bearer $TOK\"\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "binkey", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - }, - "post": { - "tags": [ - "endpoints-siteagent" - ], - "summary": "Stage an uploaded binary as a new version. This supplies the executable every future package installs and runs as...", - "description": "Stage an uploaded binary as a new version. This supplies the executable every future package installs and runs as SYSTEM on the fleet, so it is audit-logged with the stored file's sha256\n\n**Auth:** jwt + require_permission endpoints.agentpackage\n\n**Params:** path: binkey (agent|tray); multipart/form-data: file (a Windows PE, 64 MB cap), version (strict dotted-numeric), makecurrent (default true)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/agent -H \"Authorization: Bearer $TOK\" -F file=@shopdbagent.exe -F version=1.6.3\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "binkey", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: binkey (agent|tray); multipart/form-data: file (a Windows PE, 64 MB cap), version (strict dotted-numeric), makecurrent (default true)" - } - } - } - } - } - }, - "/api/endpoints/siteagent/binaries/{binkey}/detect": { - "post": { - "tags": [ - "endpoints-siteagent" - ], - "summary": "Read the version out of an uploaded PE's resource table so the upload form can prefill it. Stores nothing", - "description": "Read the version out of an uploaded PE's resource table so the upload form can prefill it. Stores nothing\n\n**Auth:** jwt + require_permission endpoints.agentpackage\n\n**Params:** path: binkey; multipart/form-data: file\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/agent/detect -H \"Authorization: Bearer $TOK\" -F file=@shopdbagent.exe\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "binkey", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: binkey; multipart/form-data: file" - } - } - } - } - } - }, - "/api/endpoints/siteagent/binaries/{binkey}/adopt": { - "post": { - "tags": [ - "endpoints-siteagent" - ], - "summary": "Adopt a binary already staged on disk into the version manifest, for a build that predates the store", - "description": "Adopt a binary already staged on disk into the version manifest, for a build that predates the store\n\n**Auth:** jwt + require_permission endpoints.agentpackage\n\n**Params:** path: binkey; body: version\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/agent/adopt -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"version\":\"1.6.2\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "binkey", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: binkey; body: version" - } - } - } - } - } - }, - "/api/endpoints/siteagent/binaries/{binkey}/current": { - "put": { - "tags": [ - "endpoints-siteagent" - ], - "summary": "Point the current staged build at another version, so a package rolls back to it. The retention sweep never prunes the...", - "description": "Point the current staged build at another version, so a package rolls back to it. The retention sweep never prunes the current one\n\n**Auth:** jwt + require_permission endpoints.agentpackage\n\n**Params:** path: binkey; body: version (must already be staged)\n\n**Example:**\n```\ncurl -X PUT http://localhost:5001/api/endpoints/siteagent/binaries/agent/current -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"version\":\"1.6.2\"}'\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "binkey", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: binkey; body: version (must already be staged)" - } - } - } - } - } - }, - "/api/shopdbbackups": { - "get": { - "tags": [ - "core-shopdbbackups" - ], - "summary": "List every retained backup set, newest first, each with a compatibility flag saying whether it restores onto this build", - "description": "List every retained backup set, newest first, each with a compatibility flag saying whether it restores onto this build\n\n**Auth:** admin\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/shopdbbackups\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - }, - "post": { - "tags": [ - "core-shopdbbackups" - ], - "summary": "Kick off an on-demand backup run. Requires the backup overlay: without a configured backups root there is nothing...", - "description": "Kick off an on-demand backup run. Requires the backup overlay: without a configured backups root there is nothing behind it\n\n**Auth:** admin\n\n**Params:** none\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/shopdbbackups\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "none" - } - } - } - } - } - }, - "/api/shopdbbackups/{snapshotid}": { - "get": { - "tags": [ - "core-shopdbbackups" - ], - "summary": "Return one set's full manifest plus a resolved compatibility verdict", - "description": "Return one set's full manifest plus a resolved compatibility verdict\n\n**Auth:** admin\n\n**Params:** path: snapshotid\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/shopdbbackups/2026-09-06T120000Z\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "snapshotid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ] - } - }, - "/api/shopdbbackups/{snapshotid}/restore": { - "post": { - "tags": [ - "core-shopdbbackups" - ], - "summary": "Queue a restore of a set, gated on schema compatibility. DESTRUCTIVE: the load drops and replaces every table", - "description": "Queue a restore of a set, gated on schema compatibility. DESTRUCTIVE: the load drops and replaces every table\n\n**Auth:** admin\n\n**Params:** path: snapshotid\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/shopdbbackups/2026-09-06T120000Z/restore\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - }, - "404": { - "description": "No such record." - } - }, - "parameters": [ - { - "name": "snapshotid", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "path: snapshotid" - } - } - } - } - } - }, - "/api/shopdbbackups/finalize-restore": { - "post": { - "tags": [ - "core-shopdbbackups" - ], - "summary": "Queue the post-restore schema upgrade for the in-container finalizer, and retry it if a previous attempt failed. Does...", - "description": "Queue the post-restore schema upgrade for the in-container finalizer, and retry it if a previous attempt failed. Does NOT run the upgrade in the request: that watcher runs it out of band, so a restore completes whether or not the browser that started it is still open, and Alembic cannot be killed by the worker timeout mid-chain\n\n**Auth:** admin\n\n**Params:** none\n\n**Example:**\n```\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/shopdbbackups/finalize-restore\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "none" - } - } - } - } - } - }, - "/api/shopdbbackups/status": { - "get": { - "tags": [ - "core-shopdbbackups" - ], - "summary": "Live status of the most recent backup and restore runs, or idle when neither is moving", - "description": "Live status of the most recent backup and restore runs, or idle when neither is moving\n\n**Auth:** admin\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/shopdbbackups/status\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/shopdbbackups/history": { - "get": { - "tags": [ - "core-shopdbbackups" - ], - "summary": "Recorded backup and restore run history, newest first", - "description": "Recorded backup and restore run history, newest first\n\n**Auth:** admin\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/shopdbbackups/history\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - } - }, - "/api/shopdbbackups/settings": { - "get": { - "tags": [ - "core-shopdbbackups" - ], - "summary": "Read the backup settings, with secret values masked", - "description": "Read the backup settings, with secret values masked\n\n**Auth:** admin\n\n**Params:** none\n\n**Example:**\n```\ncurl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/shopdbbackups/settings\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - } - }, - "put": { - "tags": [ - "core-shopdbbackups" - ], - "summary": "Update the backup settings. A masked secret submitted unchanged leaves the stored value alone", - "description": "Update the backup settings. A masked secret submitted unchanged leaves the stored value alone\n\n**Auth:** admin\n\n**Params:** body: backup settings keys\n\n**Example:**\n```\ncurl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"backup_schedule\":\"0 2 * * *\"}' http://localhost:5001/api/shopdbbackups/settings\n```", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success. Body is the success_response envelope: {status, data, meta}.", - "content": { - "application/json": { - "$ref": "#/components/schemas/SuccessEnvelope" - } - } - }, - "default": { - "description": "Error. Body is the error envelope; the code and message are nested under data.error.", - "content": { - "application/json": { - "$ref": "#/components/schemas/ErrorEnvelope" - } - } - }, - "401": { - "description": "Missing or invalid credentials." - }, - "403": { - "description": "Authenticated, but not permitted." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "body: backup settings keys" + "description": "body: JSON (fields not yet documented)" } } } diff --git a/scripts/gen_openapi.py b/scripts/gen_openapi.py index c0692c37..267714ac 100644 --- a/scripts/gen_openapi.py +++ b/scripts/gen_openapi.py @@ -1,21 +1,54 @@ -"""Generate docs/openapi.json from docs/api-inventory.json. +"""Generate docs/openapi.json from docs/api-inventory.json, and refresh that +inventory from the routes the app actually serves. The inventory is a list of {surface, endpoints:[{method,path,auth,params,purpose, -example}]} objects (one per API surface). It is HAND-MAINTAINED - nothing -generates it, and the docstring here used to imply otherwise. That is deliberate: -its value is the prose, and what an endpoint is FOR cannot be derived from the -code. Coverage is enforced instead, by tests/test_api_inventory_parity.py, which -fails when a served route has no entry. +example}]} objects (one per API surface). Two halves, maintained differently: -So: describe the endpoint in api-inventory.json, then re-run this. Served -interactively at /api/docs (see shopdb/core/api/docs.py). + STRUCTURE (surface, method, path, auth) is DERIVED. It is walked out of the + live url_map and the decorators on each view, so it cannot drift from the + code. It had: this branch carried no inventory at all, so the generator had no + input, the parity test skipped, and 24 routes served traffic documented + nowhere. - venv/bin/python scripts/gen_openapi.py + PROSE (purpose, params, example) is HUMAN. What an endpoint is FOR cannot be + derived, so a refresh never overwrites text that is already there - it fills + in a new route from its docstring and leaves every existing entry's words + alone. + +So: add a route, run --refresh, and replace the seeded prose with the reason the +route exists. Coverage is enforced on top of that by +tests/test_api_inventory_parity.py, which fails when a served route has no entry +AND when a committed auth string disagrees with what this file re-derives. +Served interactively at /api/docs (see shopdb/core/api/docs.py). + + venv/bin/python scripts/gen_openapi.py # spec from the inventory + venv/bin/python scripts/gen_openapi.py --refresh # inventory first, then spec + +THE AUTH STRING IS A GRAMMAR, not free text. Its first word names the mechanism +and everything after it is prose; '-optional' on that word means the gate can be +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. """ import json import os import re +import sys HERE = os.path.dirname(os.path.abspath(__file__)) REPO = os.path.dirname(HERE) @@ -68,23 +101,62 @@ def _norm_path(p): return normalised +# The machine-readable half of an auth string is its FIRST word. Everything +# after it is prose for a human reader. An optional gate is spelled by suffixing +# that word with '-optional'. +# +# Substring matching is what this replaces, and it is why nine operations +# published `security: []` - "no credentials needed" - while their handler +# required one. 'none (single-use bootstrap token...)' starts with 'none', so +# the old sweep published the two bootstrap routes as open, and one of them +# hands out the installable agent package and spends a use. A leading token +# cannot be fooled by the prose that explains it. +AUTH_SCHEME = { + 'jwt': 'bearerAuth', + 'permission': 'bearerAuth', + 'api-key': 'apiKeyAuth', + 'setup-code': 'setupCodeAuth', +} + +OPTIONAL_SUFFIX = '-optional' + + +def _auth_head(auth): + """The mechanism word at the front of an auth string, lowercased.""" + match = re.match(r'[a-z-]+', (auth or '').strip().lower()) + return match.group(0) if match else '' + + def _security(auth): """OpenAPI security for one endpoint's documented auth string. - `jwt-optional` is the interesting case, and the common one - 121 endpoints. - It means "works logged out, returns more when logged in", which OpenAPI - expresses as a list containing BOTH the empty requirement and the scheme. - Publishing those as bearer-required told every reader, human and machine, - that a public endpoint needs a token. + Optional auth is the interesting case. It means "works without a + credential, does more with one", which OpenAPI expresses as a list carrying + BOTH the empty requirement and the scheme. Two shapes reach it: + + jwt-optional works signed out; the handler decides what shows. + api-key-optional token OR network. A kiosk admits either the per-device + token or a source address inside the site's CIDR + allowlist, so an allowlisted caller sends no + credential. Publishing that as `[]` is not a lie, but + it is not the truth either: it tells a reader there is + no token to send, and the allowlist is invisible. + Optional says both halves exist. + + An unrecognised word publishes as bearer-required, which is the safe + direction to be wrong in: a reader who brings a token they did not need + loses nothing, a reader told none was needed is locked out on the day. """ - a = (auth or '').lower() - if 'optional' in a: - return [{}, {'bearerAuth': []}] - if a in ('none', 'public', '') or a.startswith('none') or a.startswith('public'): + head = _auth_head(auth) + optional = head.endswith(OPTIONAL_SUFFIX) + if optional: + head = head[:-len(OPTIONAL_SUFFIX)] + if not head or head in ('none', 'public'): return [] - if 'api-key' in a or 'api_key' in a or 'x-api-key' in a: - return [{'apiKeyAuth': []}] - return [{'bearerAuth': []}] + scheme = AUTH_SCHEME.get(head, 'bearerAuth') + if optional: + return [{scheme: []}, {}] + return [{scheme: []}] def _path_parameters(rawpath): @@ -237,7 +309,10 @@ def build(surfaces): '`success_response` envelope: `{status, data, meta}`. Auth: Bearer ' 'JWT (login or a managed PAT) for `jwt`/`admin`/`permission:*`; ' '`X-API-Key` for collector/managed-token endpoints; public endpoints ' - 'need neither.'), + 'need neither. A permission-gated route is published as ' + 'bearer-required, which is what a default install does - a site ' + 'that grants the permission to the `guest` role makes that same ' + 'route answer a caller with no token.'), }, # One site's production hostname was hardcoded here, so every generated # spec published it to the public wiki and offered a second site a @@ -302,18 +377,478 @@ def build(surfaces): 'securitySchemes': { 'bearerAuth': {'type': 'http', 'scheme': 'bearer', 'bearerFormat': 'JWT', 'description': 'Login token or a managed Personal Access Token (scoped).'}, + # One scheme covers every non-JWT header credential in this API, + # because the header NAME varies per surface and each operation's + # **Auth:** line says which one it wants. Publishing a scheme per + # header would be tidier and no more informative. 'apiKeyAuth': {'type': 'apiKey', 'in': 'header', 'name': 'X-API-Key', - 'description': 'Managed service token (e.g. collector.ingest, geenforce.fetch).'}, + 'description': 'A managed service or device token in a header. ' + 'X-API-Key for collector and managed tokens ' + '(collector.ingest, geenforce.fetch); ' + 'X-Endpoint-Bootstrap-Token for agent bootstrap; ' + 'X-Kiosk-Token for a kiosk screen. The operation ' + 'description names the header and, where the gate ' + 'is token-OR-network, the allowlist that is the ' + 'other half.'}, + 'setupCodeAuth': {'type': 'apiKey', 'in': 'query', 'name': 'code', + 'description': 'A single-use provisioning code, redeemed in the ' + 'query string because the caller is a browser being ' + 'pointed at a URL. Spent on any attempt, right or ' + 'wrong, and short-lived.'}, }}, 'tags': tags, 'paths': paths, } +# --------------------------------------------------------------------------- +# The derived half: refresh the inventory from the live url_map (--refresh) +# --------------------------------------------------------------------------- + +# Only the API. The frontend shell and its assets are served by this app but are +# not the contract this document describes, and tests/test_api_inventory_parity +# walks the same /api-only surface - an entry for anything else reports there as +# a documented route that is not served. +API_PREFIX = '/api' + +# The auth string for a route whose gate this cannot see. Deliberately not +# 'none': an unclassified route is the one case where a guess writes a lie into +# the published contract, and a site reads that contract to decide what its +# guest role and its firewall may reach. This greps, and it renders at /api/docs. +# Hand-editing the inventory does NOT clear this: auth is derived, so the next +# --refresh writes it straight back. Teaching the sweep below is the fix, which +# is also the only way the classification survives the route moving. +NEEDS_REVIEW = ('review: no gate found on the view - teach the sweep in ' + 'scripts/gen_openapi.py, or gate the view') + +# The jwt-optional wording, in one place because three branches reach it. +JWT_OPTIONAL = 'jwt-optional (works signed out; the handler decides what shows)' + +# Decorators that ARE the gate. require_permission and require_role are not +# here: they stamp _requires_permission onto the wrapper, which is read first +# and carries the permission name itself. +DECORATOR_AUTH = { + 'require_api_key': 'api-key (per-plugin collector token)', + 'require_api_key_legacy': 'api-key (collector token, legacy shared key)', + 'require_fetch_token': 'api-key (geenforce fetch token, or the IP allowlist)', + 'require_report_token': 'api-key (geenforce report token)', +} + +# Gates that live INSIDE the handler, found by the call the handler makes. An +# agent, a kiosk screen and a bootstrapping PC each authenticate on something +# that is not a JWT, so none of them can be gated by a decorator - and from +# outside the view, an inline gate and no gate at all look exactly alike. That +# is how 165 GET routes came to answer anybody. +HANDLER_AUTH = ( + ('_agent_auth_or_error', 'api-key (endpoint agent credential, or the fleet ' + 'key while sharedkeyallowed is on)'), + ('authenticate_agent', 'api-key (endpoint agent credential, or the fleet ' + 'key while sharedkeyallowed is on)'), + ('collector_ingest', 'api-key (endpoints collector token)'), + ('_check_collector_auth', 'api-key (per-plugin collector token)'), + ('collector_authorized', 'api-key (per-plugin collector token)'), + # Token OR network, either sufficient, so neither 'none' nor 'api-key' is + # the truth on its own: an allowlisted PC sends no credential at all, and a + # screen by the cabinet sends the device token from anywhere. 'none' was + # published here, which told a reader there is no token to send and left + # the allowlist invisible. Optional says both halves exist. + ('_kiosk_open', '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)"), + # REQUIRED, not 'none'. The handler reads the token and 404s without it, + # and /bootstrap/package hands out the installable agent package and spends + # a use doing it. + ('_presented_token', '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)'), + # The code IS the credential: unguessable, spent on any attempt, and gone in + # minutes. No header and no session, because the caller is a browser being + # pointed at a URL by an administrator. + ('_redeem_kiosk_setup_code', '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)'), + ('_bootstrap_allowed', 'none (first run only: refused once an admin exists)'), + ('verify_jwt_in_request', 'jwt'), +) + +# Routes whose gate is inline AND conditional, where the call sweep above would +# name the wrong thing. One line each, and the line is the point: these are the +# handlers where "what authenticates this?" has an answer no sweep can derive. +INLINE_AUTH = { + # The key may be absent, or invalid, and the call still proceeds: a PC + # installed without the shared key must still be able to reach an admin. A + # valid key only decides whether auto-enroll may issue on a clean match. + 'endpoints.enroll_endpoint': 'none (per-IP rate limited and capped; a valid ' + 'fleet key only decides auto-enroll)', + # The unguessable token in the path IS the authorisation - the recipient of + # a secure send has no account here. + 'tools.peek_secret': 'none (the unguessable token in the path is the ' + 'authorisation)', + 'tools.redeem_secret': 'none (the unguessable token in the path is the ' + 'authorisation; redeeming spends the secret)', + # This IS the auth surface, so a token cannot gate it. The bounds it carries + # instead are a per-IP rate limit and the 5-strike account lockout. + 'auth.login': 'none (the login surface itself; rate limited, 5-strike lockout)', +} + +# Verbs that carry a body, for the seeded params line. +BODY_VERBS = ('POST', 'PUT', 'PATCH') + + +def _literal_set(filename, name): + """One module-level set literal, read without importing the module. + + Imported, tests/test_core/test_authz.py builds a second app at import time, + which rebinds the SQLAlchemy extension out from under the one this script + is walking. + """ + import ast + try: + with open(filename, 'r', encoding='utf-8') as handle: + tree = ast.parse(handle.read()) + except (OSError, SyntaxError): + return set() + for node in tree.body: + if not isinstance(node, ast.Assign): + continue + for target in node.targets: + if isinstance(target, ast.Name) and target.id == name: + try: + return set(ast.literal_eval(node.value)) + except ValueError: + return set() + return set() + + +def _reviewed_exceptions(): + """(endpoints, blueprints) already reviewed as needing no permission. + + Read from the two sweeps in tests/ rather than restated here, so the + published contract cannot disagree with the gate that enforces it. A route + on one of those lists was argued about in front of somebody, in a file whose + whole job is to make that argument visible; a route on neither, with no + gate, is marked for review instead. That is the difference between a + decision and an oversight, which is what those lists exist to keep. + """ + tests = os.path.join(REPO, 'tests', 'test_core') + reads = os.path.join(tests, 'test_public_read_surface.py') + writes = os.path.join(tests, 'test_authz.py') + endpoints = (_literal_set(reads, 'PUBLIC_ENDPOINTS') + | _literal_set(writes, 'EXEMPT_ENDPOINTS')) + return endpoints, _literal_set(writes, 'EXEMPT_BLUEPRINTS') + + +def _source_index(filename, _cache={}): + """{function name: (decorator sources, call sources)} for one file. + + Read from the source, not from the live object: functools.wraps copies the + name and the docstring onto the wrapper and nothing else, so by the time a + view is in url_map its decorators are invisible. _requires_permission + survives only because require_permission deliberately stamps it on. + """ + 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 + for node in ast.walk(tree): + if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + continue + index[node.name] = ( + [ast.unparse(item) for item in node.decorator_list], + [ast.unparse(item) for item in ast.walk(node) + if isinstance(item, ast.Call)], + ) + _cache[filename] = index + return index + + +def _view_source(view): + """(decorators, calls) for the undecorated view behind a url_map entry.""" + import inspect + original = inspect.unwrap(view) + filename = inspect.getsourcefile(original) or '' + return _source_index(filename).get(original.__name__, ([], [])) + + +def _auth_for(view, endpoint, reviewed, blueprints): + """The honest auth string for one route, derived in gate order. + + Nothing here guesses. Every branch names a mechanism that is in the code - a + marker the decorator stamped on, a decorator in the source, a call the + handler makes, or a name on a reviewed exemption list - and a route that + matches none of them says exactly that. + """ + marker = getattr(view, '_requires_permission', None) + if marker: + roles = [name.split(':', 1)[1] for name in marker + if name.startswith('role:')] + if roles: + return 'jwt + role:' + ' or '.join(sorted(roles)) + return 'permission:' + ' or '.join(marker) + + decorators, calls = _view_source(view) + for text in decorators: + name = text.split('(', 1)[0].strip().split('.')[-1] + if name in DECORATOR_AUTH: + return DECORATOR_AUTH[name] + if name == 'jwt_required': + if 'refresh=True' in text: + return 'jwt (refresh token)' + return JWT_OPTIONAL if 'optional=True' in text else 'jwt' + + if endpoint in INLINE_AUTH: + return INLINE_AUTH[endpoint] + + for call in calls: + tail = call.split('(', 1)[0].split('.')[-1] + for needle, auth in HANDLER_AUTH: + if tail != needle: + continue + # Optional verification is a read that works signed out, not a gate. + # Only the required form authenticates anybody. + if needle == 'verify_jwt_in_request' and 'optional' in call: + return JWT_OPTIONAL + return auth + + blueprint, _, shortname = endpoint.partition('.') + if endpoint in reviewed or shortname in reviewed or blueprint in blueprints: + return 'none (reviewed: named in the public-read or mutation sweep)' + return NEEDS_REVIEW + + +def _surface_for(view, endpoint): + """The tag this route is grouped under: one surface per plugin, and one per + core blueprint. + + Derived, not preserved: a route that moves into a plugin moves tag with it, + which is the whole reason the reader is looking at the tag. + """ + import inspect + module = getattr(inspect.unwrap(view), '__module__', '') or '' + if module.startswith('plugins.'): + return 'plugin-' + module.split('.')[1] + return 'core-' + endpoint.partition('.')[0] + + +def _seed_purpose(view, endpoint): + """First paragraph of the view's docstring, as a starting point. + + A docstring says what the handler does; the inventory wants what the + endpoint is FOR, which is usually more. This seeds the entry so a new route + is never published blank, and the prose is preserved from then on. + """ + import inspect + doc = inspect.getdoc(inspect.unwrap(view)) or '' + first = ' '.join(doc.split('\n\n')[0].split()) + return first or 'UNDOCUMENTED: %s has no docstring.' % endpoint + + +def _path_arguments(rule): + """[(name, converter)] for one rule, in the order they appear.""" + return [(name, converter or 'string') for converter, name + in re.findall(r'<(?:([^:>]+):)?([^>]+)>', rule)] + + +def _seed_params(rule, methods): + parts = [] + arguments = _path_arguments(rule) + if arguments: + parts.append('path: ' + ', '.join( + '%s (%s)' % (name, converter) for name, converter in arguments)) + if any(method in BODY_VERBS for method in methods): + parts.append('body: JSON (fields not yet documented)') + # Path arguments are in the rule; query arguments are read out of the + # handler and cannot be derived, so an entry that has neither says it does + # not know rather than claiming the route takes nothing. + return '; '.join(parts) if parts else 'not documented' + + +def _seed_example(rule, methods, auth): + """A curl that is the right SHAPE for this route: its verb, a filled path + and the header its auth actually wants. + + Placeholder ids, because the generator has no database - but a reader who + substitutes one gets a call that works, which is more than a blank field + gives them. + """ + def fill(match): + converter = (match.group(1) or '').split('(')[0] + if converter.startswith(('int', 'float')): + return '1' + return 'example.png' if converter == 'path' else 'x' + + url = 'http://localhost:5001' + re.sub(r'<(?:([^:>]+):)?[^>]+>', fill, rule) + method = sorted(methods)[0] if methods else 'GET' + command = 'curl' if method == 'GET' else 'curl -X ' + method + header = '' + if auth.startswith(('permission:', 'jwt')): + header = ' -H "Authorization: Bearer $TOK"' + elif auth.startswith('api-key'): + # The header name varies per surface (X-API-Key, X-Kiosk-Token, + # X-Endpoint-Bootstrap-Token), and the auth string names the one this + # route wants. A pasted example with the wrong header is worse than + # none: it looks like it should work. + named = re.search(r'\bX-[A-Za-z0-9-]+\b', auth) + header = ' -H "%s: $KEY"' % (named.group(0) if named else 'X-API-Key') + elif auth.startswith('setup-code'): + url += '?code=$CODE' + body = '' + if any(verb in methods for verb in BODY_VERBS): + body = " -H 'Content-Type: application/json' -d '{}'" + return '%s %s%s%s' % (command, url, header, body) + + +def _served_routes(app): + """Every /api rule, as (path, sorted methods, endpoint, view). + + HEAD and OPTIONS are Werkzeug's, not the product's. A rule with several + verbs stays one entry, written 'PUT|PATCH', which is how the inventory has + always spelled it and what the parity test splits on. + """ + routes = [] + for rule in app.url_map.iter_rules(): + if rule.endpoint == 'static' or not rule.rule.startswith(API_PREFIX): + continue + methods = sorted((rule.methods or set()) - {'HEAD', 'OPTIONS'}) + if not methods: + continue + routes.append((rule.rule, methods, rule.endpoint, + app.view_functions[rule.endpoint])) + return sorted(routes, key=lambda row: (row[0], row[1])) + + +def _existing_prose(): + """{(path, METHOD): entry} for whatever the inventory already says. + + Keyed per method rather than per entry so prose survives a route that gains + or loses a verb: 'PUT' still finds the words written when the entry said + 'PUT|PATCH'. + """ + prose = {} + if not os.path.isfile(INVENTORY): + return prose + with open(INVENTORY, 'r', encoding='utf-8') as handle: + surfaces = json.load(handle) + for surface in surfaces: + for entry in surface.get('endpoints', []): + path = entry.get('path') or '' + for method in re.split(r'[/,|]', entry.get('method') or 'GET'): + method = method.strip().upper() + if method: + prose.setdefault((path, method), entry) + return prose + + +def _inventory_app(): + """The app, with every bundled plugin enabled. + + The inventory describes the PRODUCT, not one site's switched-on subset: a + plugin disabled on the machine that happens to run this would drop its whole + surface out of the published contract without a word, and the next refresh + somewhere else would put it back. So a disabled plugin is refused here + rather than quietly omitted. + """ + import sys + # .env BEFORE create_app, not after: config is read while the app is built, + # so a load underneath it changes nothing. A plain `python scripts/...` run + # gets neither wsgi.py's load nor the flask CLI's, and falls back to the + # hardcoded root@localhost URL - which surfaces as a bare 1045 access-denied + # that says nothing about the real cause. + from dotenv import load_dotenv + load_dotenv(os.path.join(REPO, '.env')) + os.environ.setdefault('FLASK_ENV', 'testing') + if REPO not in sys.path: + sys.path.insert(0, REPO) + from shopdb import create_app + app = create_app('testing') + plugins = os.path.join(REPO, 'plugins') + bundled = sorted( + name for name in os.listdir(plugins) + if os.path.isfile(os.path.join(plugins, name, 'manifest.json'))) + manager = app.extensions.get('plugin_manager') + off = [name for name in bundled + if not (manager and manager.registry.is_enabled(name))] + if off: + raise SystemExit( + 'these bundled plugins are disabled, so their routes would be ' + 'missing\nfrom the inventory: %s\nenable them and re-run:\n' + ' FLASK_APP=shopdb venv/bin/flask plugins enable %s' + % (', '.join(off), off[0])) + return app + + +def refresh_inventory(): + """Rewrite the inventory's derived half from the routes the app serves. + + Structure (surface, method, path, auth) is replaced outright. Prose + (purpose, params, example) is carried over untouched wherever an entry for + that route already exists, and seeded from the view's docstring where one + does not. + """ + app = _inventory_app() + reviewed, blueprints = _reviewed_exceptions() + prose = _existing_prose() + + surfaces = {} + seeded = [] + unclassified = [] + for path, methods, endpoint, view in _served_routes(app): + auth = _auth_for(view, endpoint, reviewed, blueprints) + if auth == NEEDS_REVIEW: + unclassified.append('%s %s (%s)' % ('|'.join(methods), path, endpoint)) + known = prose.get((path, methods[0])) + if known is None: + seeded.append('%s %s' % ('|'.join(methods), path)) + entry = dict(known or {}) + entry.update({ + 'method': '|'.join(methods), + 'path': path, + 'auth': auth, + 'params': entry.get('params') or _seed_params(path, methods), + 'purpose': entry.get('purpose') or _seed_purpose(view, endpoint), + 'example': entry.get('example') or _seed_example(path, methods, auth), + }) + # 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')} + ordered.update({key: value for key, value in entry.items() + if key not in ordered}) + surfaces.setdefault(_surface_for(view, endpoint), []).append(ordered) + + 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') + + total = sum(len(surface['endpoints']) for surface in document) + print('refreshed %s: %d entries in %d surfaces (%d new)' + % (INVENTORY, total, len(document), len(seeded))) + for line in seeded: + print(' seeded %s' % line) + if unclassified: + print('%d route(s) have no gate this can see. Classify each one by hand ' + 'in the inventory, or gate it:' % len(unclassified)) + for line in unclassified: + print(' review %s' % line) + + def main(): - surfaces = json.load(open(INVENTORY)) + if '--refresh' in sys.argv[1:]: + refresh_inventory() + with open(INVENTORY, 'r', encoding='utf-8') as handle: + surfaces = json.load(handle) spec = build(surfaces) - json.dump(spec, open(OUT, 'w'), indent=1) + with open(OUT, 'w', encoding='utf-8') as handle: + json.dump(spec, handle, indent=1) ops = sum(len(v) for v in spec['paths'].values()) print('wrote %s: %d paths, %d operations from %d surfaces' % (OUT, len(spec['paths']), ops, len(surfaces))) diff --git a/tests/test_api_inventory_parity.py b/tests/test_api_inventory_parity.py index 5742694d..f8637fc1 100644 --- a/tests/test_api_inventory_parity.py +++ b/tests/test_api_inventory_parity.py @@ -1,20 +1,26 @@ """Every route the app serves is described in docs/api-inventory.json. -The inventory is hand-maintained, and deliberately so: its value is the prose - -what each endpoint is FOR, what it accepts, a worked example - and none of that -can be derived from the code. An audit of all 372 documented operations found -zero phantom routes and zero wrong parameter names, so the maintenance itself is -in good order. +The inventory has two halves. Its STRUCTURE - surface, method, path and the auth +each route actually enforces - is derived from the live url_map and the +decorators on each view by `gen_openapi.py --refresh`. Its PROSE - what an +endpoint is FOR, what it accepts, a worked example - is human, and a refresh +never overwrites words that are already there. -What it cannot do is notice a route somebody added. Twenty-two were serving -traffic undocumented, including an entire plugin's surface and every one of the -dashboard card endpoints - each of them added after its surface was written. +This module is the coverage half of that, and it is the half a generator cannot +do for you: a refresh describes whatever is served at the moment it runs, so +without a gate the file rots the moment the next route lands. -So the prose stays hand-written and the COVERAGE is enforced here. Adding an -endpoint now means describing it, in the same commit, which is when the author -still knows what it is for. +It had. On this branch the inventory was not tracked at all, so the skipif below +fired, this module reported green while checking nothing, and 24 routes went to +production documented nowhere - including the whole endpoint-agent delivery +surface and both kiosks. scripts/check-gates-ran.sh now asserts this module RAN, +in every checkout rather than only in one with wiki prose in it. + +So: add a route, run `gen_openapi.py --refresh`, and replace the seeded purpose +with the reason the route exists - which is while the author still knows it. """ +import importlib.util import json import re from pathlib import Path @@ -93,3 +99,87 @@ def test_nothing_documented_has_been_removed(): assert not stale, ( '%d documented route(s) are no longer served:\n %s' % (len(stale), '\n '.join('%s %s' % row for row in stale))) + + +# --------------------------------------------------------------------------- +# The other half: the auth string, which nothing gated until now +# --------------------------------------------------------------------------- +# +# The two tests above check that a route EXISTS in both directions. Nothing +# checked what the entry SAYS about it, so a wrong or stale auth string passed +# every gate in the repo - and auth is the field a site reads to decide what its +# guest role and its firewall may reach, and the field the published spec turns +# into a machine-readable security requirement. +# +# It had rotted exactly that way: nine operations documented as needing no +# credential while the handler required one, and a kiosk route documented as +# ungated when its gate is a single-use setup code. Both survived a refresh, +# because a refresh only rewrites; nothing compared. +# +# So this re-derives auth from the live app with the generator's own classifier +# and fails on any disagreement with the committed file. The derivation is +# authoritative: hand-editing an auth string in the inventory does not survive +# the next --refresh, so the fix for a wrong one is always to teach the sweep in +# scripts/gen_openapi.py, or to gate the view. + + +def _generator(): + """scripts/gen_openapi.py, loaded by path. + + It is a script, not a package module, so there is nothing to import. Loading + it runs its imports and nothing else - it builds no app until it is asked + to. + """ + path = REPO / 'scripts' / 'gen_openapi.py' + spec = importlib.util.spec_from_file_location('gen_openapi', path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def derived_auth(): + """{(METHOD, path): auth} as the classifier reads the code right now.""" + generator = _generator() + app = create_app('testing') + reviewed, blueprints = generator._reviewed_exceptions() + derived = {} + for rule, methods, endpoint, view in generator._served_routes(app): + auth = generator._auth_for(view, endpoint, reviewed, blueprints) + for method in methods: + derived[(method, normalise(rule))] = auth + return derived + + +def documented_auth(): + """{(METHOD, path): auth} as docs/api-inventory.json 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('auth') or '' + return documented + + +def test_documented_auth_is_what_the_code_enforces(): + """A stale auth string is a lie about who may call a route. + + Only routes present on both sides are compared. A route missing from either + is a coverage fault, and the two tests above already name it; reporting it + twice here would bury this one. + """ + derived = derived_auth() + documented = documented_auth() + disagreements = [] + for key in sorted(set(derived) & set(documented)): + if derived[key] != documented[key]: + disagreements.append( + '%s %s\n documented: %s\n enforced: %s' + % (key[0], key[1], documented[key] or '(blank)', derived[key])) + assert not disagreements, ( + '%d route(s) document an auth that is not what the code enforces. The ' + '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]))) diff --git a/tests/test_openapi_spec.py b/tests/test_openapi_spec.py index 82a8191f..45eb1570 100644 --- a/tests/test_openapi_spec.py +++ b/tests/test_openapi_spec.py @@ -72,15 +72,83 @@ def test_write_verbs_can_carry_a_body(spec): 'from this spec cannot send one:\n ' + '\n '.join(offenders[:15])) +def documented_auth(operation): + """The auth string the generator wrote into this operation's description. + + The spec is the only thing this module reads, and the description is where + the human-readable auth lands ('**Auth:** jwt-optional (...)'). Comparing + the published `security` against it is how a lie gets caught: the two come + from the same inventory entry and must agree. + """ + match = re.search(r'\*\*Auth:\*\*\s*(.+)', operation.get('description') or '') + return match.group(1).strip().lower() if match else '' + + def test_optional_auth_is_expressed_as_optional(spec): - """An endpoint that works logged out must not be published as requiring a - token - that is a lie to every reader, and it is the majority case here.""" - optional = [operation for _, _, operation in operations(spec) - if any(item == {} for item in operation.get('security') or [])] - assert len(optional) > 50, ( - 'The inventory documents over a hundred jwt-optional endpoints; the ' - 'spec should express them with an empty security requirement ' - 'alongside the scheme, not as bearer-required.') + """A route whose auth is optional is published as optional, either way round. + + WHY THE COUNT IS GONE. This asserted `len(optional) > 50`, on the premise + that optional auth was the majority case here - it said so in as many + words. It is not any more. The guest role and the permission model moved + the read surface to `permission:*`, which publishes as bearer-required and + is correct, and the optional count fell to 14. The assertion went red + without a single route being wrong. + + A count was never the property anyone cares about. It goes red when the + codebase legitimately changes shape, and it stays green while an individual + optional route is published as bearer-required, which is the actual defect. + Lowering the number to 14 would only buy time until the next legitimate + move. So the premise is dropped and the per-route property is checked + instead, whatever the total: an operation whose auth says optional carries + BOTH the empty requirement and a scheme, and an operation that publishes + the empty requirement says optional in its auth. + """ + offenders = [] + for path, verb, operation in operations(spec): + auth = documented_auth(operation) + security = operation.get('security') or [] + openly = any(item == {} for item in security) + scheme = any(item for item in security) + if 'optional' in auth: + if not openly: + offenders.append( + '%s %s: auth says optional, published as required (%r)' + % (verb.upper(), path, security)) + elif not scheme: + offenders.append( + '%s %s: auth says optional, published with no scheme at ' + 'all, so a caller is never told what the credential is' + % (verb.upper(), path)) + elif openly: + offenders.append( + '%s %s: published as optional, but its auth says %r' + % (verb.upper(), path, auth)) + assert not offenders, ( + 'Optional auth means "works without a credential, does more with one", ' + 'and OpenAPI writes it as the empty requirement ALONGSIDE the ' + 'scheme:\n ' + '\n '.join(offenders[:15])) + + +def test_an_operation_needing_no_credential_says_so_in_its_auth(spec): + """`security: []` is a machine-readable claim that no credential is needed. + + Nine operations published it while their handler required one, including + the two that hand a bootstrapping PC its agent package - `security: []` and + a 404 for anyone who does not present the single-use token. The generator + reached that by substring: the auth string started with the word 'none', in + a sentence that went on to name the token. This is the gate on that class + of defect from the spec side, where a reader or a generated client sees it. + """ + offenders = [ + '%s %s: %r' % (verb.upper(), path, documented_auth(operation)) + for path, verb, operation in operations(spec) + if not operation.get('security') + and 'none' not in documented_auth(operation) + and 'public' not in documented_auth(operation) + ] + assert not offenders, ( + 'These operations publish "no credentials needed" while their auth ' + 'names a credential:\n ' + '\n '.join(offenders[:15])) def test_an_endpoint_that_needs_no_token_cannot_fail_with_401(spec):