From 05be4c44892dfc76d9babea7447adcb1c7491a27 Mon Sep 17 00:00:00 2001 From: cproudlock Date: Fri, 14 Aug 2026 15:43:27 -0400 Subject: [PATCH] docs: fix the examples that fail if you paste them Six procedures that could not be followed as written. Eighty-nine curl examples single-quoted `Authorization: Bearer $TOKEN`, so the shell never expanded it and the server answered 422 "Not enough segments". Nine more did the same with X-API-Key. The other 129 examples in the same file already used double quotes, so this was drift rather than a convention, and the spec regenerated from it carried the fault onward. The GE-Enforce report example put a `//` comment inside a JSON body. The server parses with silent=True, so it saw `{}` and answered "hostname is required" about a body that plainly has one - the worst kind of error message, one that sends the reader to the wrong field entirely. The IIS install ran `flask db upgrade` and a per-plugin install loop but never `flask plugin upgrade-all`, leaving every plugin's own chain unapplied. That is precisely the 1054 "Unknown column" a deploy then hits somewhere else, days later, on the page that uses the new column. The pilot runbook looped `flask plugin enable` over plugins that were not yet installed; enable refuses those, so on a fresh database it exited 1 on every iteration and enabled nothing. ADR-013 had already recorded that defect. `apply-profile` installs and enables in dependency order, which is what the step was reaching for. DEPLOY-WINDOWS-IIS named a profile file that does not exist; the shipped ones do. And PLUGIN-EXTERNAL-REPO never mentioned PLUGIN_TABLE_OWNERS, while the migration engine raises for any plugin missing from it - so the guide's own step 5 fails for any external plugin that owns a table. That the registry lives in the framework repo is deliberate, so the guide now says so, and says what it costs: a table-owning external plugin is a two-repository change, and a plugin that owns no tables avoids it entirely. --- docs/DEPLOY-WINDOWS-IIS.md | 3 +- docs/GE-ENFORCE-CLIENT.md | 5 +- docs/INSTALL-WINDOWS-IIS.md | 5 + docs/PLUGIN-EXTERNAL-REPO.md | 29 ++++ docs/api-inventory.json | 196 +++++++++++------------ docs/openapi.json | 196 +++++++++++------------ scripts/site_imports/wjf/PILOT-DEPLOY.md | 10 +- 7 files changed, 242 insertions(+), 202 deletions(-) diff --git a/docs/DEPLOY-WINDOWS-IIS.md b/docs/DEPLOY-WINDOWS-IIS.md index c783859..17ca3de 100644 --- a/docs/DEPLOY-WINDOWS-IIS.md +++ b/docs/DEPLOY-WINDOWS-IIS.md @@ -138,7 +138,8 @@ venv\Scripts\flask seed admin --username admin --email admin@yourfacility.exampl Cleaner than a hand list: declare the set once in a site profile and apply it: ```powershell -venv\Scripts\flask plugin apply-profile deploy\site-profile.json # install + enable the chosen set, in dependency order +venv\Scripts\flask plugin apply-profile deploy\site-profile-universal.json # install + enable the chosen set, in dependency order +# deploy\site-profile.example.json is the annotated starting point to copy and trim. venv\Scripts\flask plugin upgrade-all venv\Scripts\flask plugin prune-schema --yes --force # FIRST PROVISIONING ONLY - see the warning below ``` diff --git a/docs/GE-ENFORCE-CLIENT.md b/docs/GE-ENFORCE-CLIENT.md index 2e69cdd..517f104 100644 --- a/docs/GE-ENFORCE-CLIENT.md +++ b/docs/GE-ENFORCE-CLIENT.md @@ -63,6 +63,9 @@ being edited in shopdb, so a half-finished edit can never reach a PC. Each enforcement cycle, POST the result (best-effort; a failed report never fails the cycle): +`appliedversion` is the published version the client actually ran, not the +latest one available. + ``` POST /api/geenforce/report X-API-Key: @@ -70,7 +73,7 @@ POST /api/geenforce/report { "hostname": "CMMPC01", "scopename": "gea-shopfloor-cmm", - "appliedversion": 3, // the published version you actually ran + "appliedversion": 3, "enforcerversion": "2.6", "counts": { "installed": 1, "skipped": 3, "failed": 0, "filtered": 2 }, "results": [ diff --git a/docs/INSTALL-WINDOWS-IIS.md b/docs/INSTALL-WINDOWS-IIS.md index 09c404f..dbd7492 100644 --- a/docs/INSTALL-WINDOWS-IIS.md +++ b/docs/INSTALL-WINDOWS-IIS.md @@ -125,6 +125,11 @@ foreach ($p in "computers","machines","network","notifications","printers","know venv\Scripts\flask plugin install $p } +# EVERY plugin owns its own Alembic chain (ADR-008), and `flask db upgrade` +# above ran only the core one. Skipping this is how a new column reaches +# production as a 1054 "Unknown column" error on the page that uses it. +venv\Scripts\flask plugin upgrade-all + # first admin (password generated + printed once - store it): venv\Scripts\flask seed admin --username admin --email admin@yourfacility.example.com ``` diff --git a/docs/PLUGIN-EXTERNAL-REPO.md b/docs/PLUGIN-EXTERNAL-REPO.md index b9c3d17..6c21dec 100644 --- a/docs/PLUGIN-EXTERNAL-REPO.md +++ b/docs/PLUGIN-EXTERNAL-REPO.md @@ -102,6 +102,35 @@ venv/bin/python -m pytest ../wjsf-shipping/tests Edits in `../wjsf-shipping` are picked up on the next framework restart, because the symlink points back at your working tree. +### If your plugin owns tables, step 5 needs a change to the framework + +This is the one place an external plugin is not self-contained, and it is better +said plainly than discovered at the first migration. + +`flask plugin upgrade-all` builds each plugin's metadata from +`PLUGIN_TABLE_OWNERS` in `shopdb/plugins/alembic_template.py`, and raises rather +than guessing when a plugin has no entry: + +``` +RuntimeError: PLUGIN_TABLE_OWNERS has no entry for plugin 'shipping'. +Update shopdb/plugins/alembic_template.py. +``` + +So a plugin that owns tables needs its table names registered in that dictionary +in the framework repository. It is deliberate - the registry is what stops one +plugin's migration touching another's tables, and `tests/test_plugin_migrations.py` +tests it - but it does mean a table-owning external plugin is a two-repository +change: yours, plus a one-line addition upstream. + +Two ways to live with it: + +- **Send the entry upstream.** One line in `PLUGIN_TABLE_OWNERS` plus one in + `EXPECTED_HEAD_REVISION`, and your plugin is a normal citizen from then on. +- **Own no tables.** A plugin that stores nothing of its own - a report, a + dashboard card, a settings page over existing models - has nothing to + register and stays entirely in your repository. More plugins fit this than + expect to. + ## core_version pinning for sister sites The framework is pre-1.0. Under semver, any 0.x minor bump is allowed to break diff --git a/docs/api-inventory.json b/docs/api-inventory.json index a002891..ad5af10 100644 --- a/docs/api-inventory.json +++ b/docs/api-inventory.json @@ -341,7 +341,7 @@ "purpose": "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", "auth": "jwt + permission:reports.export", "params": "body: subject, columns [{key,label}], rows [{..}], intro (optional), to (optional email)", - "example": "curl -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\"}'" + "example": "curl -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\"}'" }, { "method": "GET", @@ -429,7 +429,7 @@ "purpose": "Create a display mapping; requires fqdn or ipaddress, dashboard role requires businessunitid, 409 on duplicate fqdn/IP; audit-logged", "auth": "jwt + role:admin", "params": "body: fqdn, ipaddress, displayrole (dashboard|lobby|partskiosk, default dashboard), businessunitid, description", - "example": "curl -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}'" + "example": "curl -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}'" }, { "method": "PUT", @@ -437,7 +437,7 @@ "purpose": "Update a display mapping; non-dashboard roles get businessunitid nulled, dashboard role must keep one", "auth": "jwt + role:admin", "params": "body: any of fqdn, ipaddress, displayrole, businessunitid, description", - "example": "curl -X PUT http://localhost:5001/api/dashboarddefaults/7 -H 'Authorization: Bearer $JWT' -H 'Content-Type: application/json' -d '{\"displayrole\":\"lobby\"}'" + "example": "curl -X PUT http://localhost:5001/api/dashboarddefaults/7 -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"displayrole\":\"lobby\"}'" }, { "method": "DELETE", @@ -445,7 +445,7 @@ "purpose": "Soft-delete (deactivate) a display mapping", "auth": "jwt + role:admin", "params": "none", - "example": "curl -X DELETE http://localhost:5001/api/dashboarddefaults/7 -H 'Authorization: Bearer $JWT'" + "example": "curl -X DELETE http://localhost:5001/api/dashboarddefaults/7 -H \"Authorization: Bearer $JWT\"" }, { "method": "POST", @@ -453,7 +453,7 @@ "purpose": "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", "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)", "params": "body: JSON payload whose schema identityfield (e.g. hostname) is required; rest is plugin-defined", - "example": "curl -X POST http://localhost:5001/api/collector/computers -H 'X-API-Key: $KEY' -H 'Content-Type: application/json' -d '{\"hostname\":\"WKSTN0042\",\"serialnumber\":\"ABC123\"}'" + "example": "curl -X POST http://localhost:5001/api/collector/computers -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{\"hostname\":\"WKSTN0042\",\"serialnumber\":\"ABC123\"}'" }, { "method": "GET", @@ -461,7 +461,7 @@ "purpose": "List collector schemas for all enabled plugins that accept collector input", "auth": "jwt", "params": "none", - "example": "curl http://localhost:5001/api/collector/_schemas -H 'Authorization: Bearer $JWT'" + "example": "curl http://localhost:5001/api/collector/_schemas -H \"Authorization: Bearer $JWT\"" }, { "method": "POST", @@ -469,7 +469,7 @@ "purpose": "Legacy computers-specific ingest: update one PC matched by hostname (or asset number) - lastreporteddate, lastboottime, loggedinuser, serialnumber", "auth": "api-key", "params": "body: hostname (required), lastboottime (ISO), currentuser, serialnumber", - "example": "curl -X POST http://localhost:5001/api/collector/pc -H 'X-API-Key: $KEY' -H 'Content-Type: application/json' -d '{\"hostname\":\"WKSTN0042\",\"currentuser\":\"212345678\"}'" + "example": "curl -X POST http://localhost:5001/api/collector/pc -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{\"hostname\":\"WKSTN0042\",\"currentuser\":\"212345678\"}'" }, { "method": "POST", @@ -477,7 +477,7 @@ "purpose": "Update installed applications for one PC; only apps already in the Application table are tracked, others skipped; returns created/updated/skipped counts", "auth": "api-key", "params": "body: hostname (required), apps [{appname, version}] (required)", - "example": "curl -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\"}]}'" + "example": "curl -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\"}]}'" }, { "method": "POST", @@ -485,7 +485,7 @@ "purpose": "Record PC online heartbeat (single hostname or batch); stamps lastreporteddate, returns updated count and notfound list", "auth": "api-key", "params": "body: hostname (string) or hostnames (array)", - "example": "curl -X POST http://localhost:5001/api/collector/heartbeat -H 'X-API-Key: $KEY' -H 'Content-Type: application/json' -d '{\"hostnames\":[\"pc1\",\"pc2\"]}'" + "example": "curl -X POST http://localhost:5001/api/collector/heartbeat -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{\"hostnames\":[\"pc1\",\"pc2\"]}'" }, { "method": "POST", @@ -493,7 +493,7 @@ "purpose": "Bulk update many PCs in one call (lastreporteddate, currentuser, lastboottime per entry); returns updated/notfound/errors", "auth": "api-key", "params": "body: pcs [{hostname (required), currentuser, lastboottime}]", - "example": "curl -X POST http://localhost:5001/api/collector/bulk -H 'X-API-Key: $KEY' -H 'Content-Type: application/json' -d '{\"pcs\":[{\"hostname\":\"pc1\",\"currentuser\":\"212345678\"}]}'" + "example": "curl -X POST http://localhost:5001/api/collector/bulk -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{\"pcs\":[{\"hostname\":\"pc1\",\"currentuser\":\"212345678\"}]}'" }, { "method": "GET", @@ -501,7 +501,7 @@ "purpose": "Collector API liveness/credential check; returns timestamp and the collector endpoint list", "auth": "api-key", "params": "none", - "example": "curl http://localhost:5001/api/collector/status -H 'X-API-Key: $KEY'" + "example": "curl http://localhost:5001/api/collector/status -H \"X-API-Key: $KEY\"" }, { "method": "GET", @@ -509,7 +509,7 @@ "purpose": "List audit logs with filtering and pagination, newest first; rows enriched with best-effort SSO-to-full-name resolution", "auth": "jwt + permission:audit.view", "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)", - "example": "curl 'http://localhost:5001/api/auditlogs?action=deleted&perpage=100' -H 'Authorization: Bearer $JWT'" + "example": "curl 'http://localhost:5001/api/auditlogs?action=deleted&perpage=100' -H \"Authorization: Bearer $JWT\"" }, { "method": "GET", @@ -517,7 +517,7 @@ "purpose": "Full audit history for one entity, newest first", "auth": "jwt + permission:audit.view", "params": "path only", - "example": "curl http://localhost:5001/api/auditlogs/entity/Asset/42 -H 'Authorization: Bearer $JWT'" + "example": "curl http://localhost:5001/api/auditlogs/entity/Asset/42 -H \"Authorization: Bearer $JWT\"" }, { "method": "GET", @@ -525,7 +525,7 @@ "purpose": "Audit statistics: counts by action and entity type, last-7-days activity count, top 5 most active users", "auth": "jwt + permission:audit.view", "params": "none", - "example": "curl http://localhost:5001/api/auditlogs/stats -H 'Authorization: Bearer $JWT'" + "example": "curl http://localhost:5001/api/auditlogs/stats -H \"Authorization: Bearer $JWT\"" }, { "method": "GET", @@ -549,7 +549,7 @@ "purpose": "Enable or disable a plugin (route changes need an app restart); 409 when unknown or a dependency conflict refuses the change", "auth": "jwt + role:admin", "params": "body: enabled (bool, required)", - "example": "curl -X PUT http://localhost:5001/api/plugins/warranty -H 'Authorization: Bearer $JWT' -H 'Content-Type: application/json' -d '{\"enabled\":false}'" + "example": "curl -X PUT http://localhost:5001/api/plugins/warranty -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"enabled\":false}'" }, { "method": "GET", @@ -618,7 +618,7 @@ "purpose": "List the full self-hosted directory for the management page; 400 when directory mode is external.", "auth": "jwt-optional", "params": "none", - "example": "curl -H 'Authorization: Bearer $TOKEN' 'http://localhost:5001/api/employees/directory'" + "example": "curl -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/employees/directory'" }, { "method": "POST", @@ -626,7 +626,7 @@ "purpose": "Create a self-hosted directory employee; 409 if SSO exists, 400 in external mode.", "auth": "jwt + require_role admin", "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)", - "example": "curl -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'" + "example": "curl -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'" }, { "method": "PUT", @@ -634,7 +634,7 @@ "purpose": "Update a self-hosted directory employee's name/team/role/picture; 404 if missing, 400 in external mode.", "auth": "jwt + require_role admin", "params": "sso (path); JSON body: firstname, lastname, team, role, picture (external-style keys also accepted; team/role/picture can be cleared)", - "example": "curl -X PUT -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"team\":\"Quality\"}' 'http://localhost:5001/api/employees/directory/210009518'" + "example": "curl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"team\":\"Quality\"}' 'http://localhost:5001/api/employees/directory/210009518'" }, { "method": "DELETE", @@ -642,7 +642,7 @@ "purpose": "Delete a self-hosted directory employee; 404 if missing, 400 in external mode.", "auth": "jwt + require_role admin", "params": "sso (path)", - "example": "curl -X DELETE -H 'Authorization: Bearer $TOKEN' 'http://localhost:5001/api/employees/directory/210009518'" + "example": "curl -X DELETE -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/employees/directory/210009518'" }, { "method": "POST", @@ -650,7 +650,7 @@ "purpose": "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.", "auth": "jwt + require_role admin", "params": "multipart file= OR JSON body {\"csv\": \"...\"}; rows missing numeric sso or names are skipped", - "example": "curl -X POST -H 'Authorization: Bearer $TOKEN' -F 'file=@employees.csv' 'http://localhost:5001/api/employees/directory/import'" + "example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" -F 'file=@employees.csv' 'http://localhost:5001/api/employees/directory/import'" }, { "method": "POST", @@ -658,7 +658,7 @@ "purpose": "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.", "auth": "jwt + require_role admin", "params": "sso (path); multipart/form-data file=, extensions .png/.jpg/.jpeg/.gif/.webp only", - "example": "curl -X POST -H 'Authorization: Bearer $TOKEN' -F 'file=@jane.jpg' 'http://localhost:5001/api/employees/210009518/photo'" + "example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" -F 'file=@jane.jpg' 'http://localhost:5001/api/employees/210009518/photo'" }, { "method": "GET", @@ -674,7 +674,7 @@ "purpose": "Clear a self-hosted employee's photo record and delete the uploaded file; 409 in external mode, 404 if employee missing.", "auth": "jwt + require_role admin", "params": "sso (path)", - "example": "curl -X DELETE -H 'Authorization: Bearer $TOKEN' 'http://localhost:5001/api/employees/210009518/photo'" + "example": "curl -X DELETE -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/employees/210009518/photo'" } ] }, @@ -718,7 +718,7 @@ "purpose": "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.", "auth": "permission:kb.create (jwt required)", "params": "body JSON: shortdescription (required), linkurl (required), appid (optional int, must exist), keywords (optional); import-mode may pass timestamp fields", - "example": "curl -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\"}'" + "example": "curl -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\"}'" }, { "method": "PUT", @@ -726,7 +726,7 @@ "purpose": "Update an article's shortdescription, linkurl, appid, keywords, and/or isactive; validates appid if changed; honors import timestamps; 404 if article missing.", "auth": "permission:kb.edit (jwt required)", "params": "path: link_id (int); body JSON: any of shortdescription, linkurl, appid, keywords, isactive", - "example": "curl -X PUT 'http://localhost:5001/api/knowledgebase/42' -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"keywords\":\"vpn,zscaler\",\"isactive\":true}'" + "example": "curl -X PUT 'http://localhost:5001/api/knowledgebase/42' -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"keywords\":\"vpn,zscaler\",\"isactive\":true}'" }, { "method": "DELETE", @@ -734,7 +734,7 @@ "purpose": "Soft-delete an article by setting isactive=false (row is retained); 404 if article missing.", "auth": "permission:kb.delete (jwt required)", "params": "path: link_id (int)", - "example": "curl -X DELETE 'http://localhost:5001/api/knowledgebase/42' -H 'Authorization: Bearer $TOKEN'" + "example": "curl -X DELETE 'http://localhost:5001/api/knowledgebase/42' -H \"Authorization: Bearer $TOKEN\"" } ], "surface": "plugin-knowledgebase" @@ -1496,7 +1496,7 @@ "auth": "jwt-optional", "params": "page, per_page, active (default true; 'false' includes inactive), search (ilike on computertype)", "purpose": "List computer types, paginated, active-only by default", - "example": "curl -H 'Authorization: Bearer $TOKEN' 'http://localhost:5001/api/computers/types?search=kiosk&per_page=50'" + "example": "curl -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/computers/types?search=kiosk&per_page=50'" }, { "method": "GET", @@ -1512,7 +1512,7 @@ "auth": "permission:computers.create (jwt_required)", "params": "body: computertype (required), description, icon, color; matching a deactivated type revives it instead of 409", "purpose": "Create (or reactivate) a computer type", - "example": "curl -X POST -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"computertype\":\"Shopfloor\",\"color\":\"#0066cc\"}' 'http://localhost:5001/api/computers/types'" + "example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"computertype\":\"Shopfloor\",\"color\":\"#0066cc\"}' 'http://localhost:5001/api/computers/types'" }, { "method": "PUT", @@ -1520,7 +1520,7 @@ "auth": "permission:computers.edit (jwt_required)", "params": "body: computertype, description, icon, color, isactive; 409 on duplicate name", "purpose": "Update a computer type", - "example": "curl -X PUT -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"isactive\":false}' 'http://localhost:5001/api/computers/types/3'" + "example": "curl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"isactive\":false}' 'http://localhost:5001/api/computers/types/3'" }, { "method": "DELETE", @@ -1528,7 +1528,7 @@ "auth": "permission:computers.delete (jwt_required)", "params": "type_id in path; 409 if any Computer still uses the type", "purpose": "Hard-delete a computer type when unused", - "example": "curl -X DELETE -H 'Authorization: Bearer $TOKEN' 'http://localhost:5001/api/computers/types/3'" + "example": "curl -X DELETE -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/computers/types/3'" }, { "method": "GET", @@ -1544,7 +1544,7 @@ "auth": "permission:computers.edit (jwt_required)", "params": "body: name, scheme, linktemplate (all required), defaultport, isactive; 409 on duplicate name", "purpose": "Create an access protocol", - "example": "curl -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'" + "example": "curl -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'" }, { "method": "PUT|PATCH", @@ -1552,7 +1552,7 @@ "auth": "permission:computers.edit (jwt_required)", "params": "body: name, scheme, linktemplate, defaultport, isactive (all optional)", "purpose": "Update an access protocol", - "example": "curl -X PATCH -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"defaultport\":5901}' 'http://localhost:5001/api/computers/protocols/2'" + "example": "curl -X PATCH -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"defaultport\":5901}' 'http://localhost:5001/api/computers/protocols/2'" }, { "method": "DELETE", @@ -1560,7 +1560,7 @@ "auth": "permission:computers.edit (jwt_required)", "params": "protocol_id in path; if referenced by any ComputerAccess it deactivates instead of deleting", "purpose": "Delete an access protocol (soft-deactivate when in use)", - "example": "curl -X DELETE -H 'Authorization: Bearer $TOKEN' 'http://localhost:5001/api/computers/protocols/2'" + "example": "curl -X DELETE -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/computers/protocols/2'" }, { "method": "GET", @@ -1568,7 +1568,7 @@ "auth": "jwt-optional", "params": "none; uses pctype mapping for gea-shopfloor-display (default 'Kiosk') and display_fqdn_domain setting", "purpose": "List display-kiosk computers with derived F. FQDN for the Dashboard Defaults picker", - "example": "curl -H 'Authorization: Bearer $TOKEN' 'http://localhost:5001/api/computers/display-kiosks'" + "example": "curl -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/computers/display-kiosks'" }, { "method": "GET", @@ -1608,7 +1608,7 @@ "auth": "permission:computers.create (jwt_required)", "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", "purpose": "Create a computer (Asset + Computer records, optional primary IP and access methods), audit-logged", - "example": "curl -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'" + "example": "curl -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'" }, { "method": "PUT", @@ -1616,7 +1616,7 @@ "auth": "permission:computers.edit (jwt_required)", "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", "purpose": "Update a computer's asset and extension fields", - "example": "curl -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'" + "example": "curl -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'" }, { "method": "DELETE", @@ -1624,7 +1624,7 @@ "auth": "permission:computers.delete (jwt_required)", "params": "computer_id in path", "purpose": "Soft-delete a computer (sets asset isactive=false), audit-logged", - "example": "curl -X DELETE -H 'Authorization: Bearer $TOKEN' 'http://localhost:5001/api/computers/42'" + "example": "curl -X DELETE -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/computers/42'" }, { "method": "GET", @@ -1640,7 +1640,7 @@ "auth": "permission:computers.create (jwt_required)", "params": "body: appid (required, must exist in Applications), appversionid; reactivates a soft-deleted install; 409 if already installed", "purpose": "Record an application install on a computer", - "example": "curl -X POST -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"appid\":17,\"appversionid\":3}' 'http://localhost:5001/api/computers/42/apps'" + "example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"appid\":17,\"appversionid\":3}' 'http://localhost:5001/api/computers/42/apps'" }, { "method": "DELETE", @@ -1648,7 +1648,7 @@ "auth": "permission:computers.delete (jwt_required)", "params": "computer_id and app_id in path", "purpose": "Soft-remove an installed application (isactive=false)", - "example": "curl -X DELETE -H 'Authorization: Bearer $TOKEN' 'http://localhost:5001/api/computers/42/apps/17'" + "example": "curl -X DELETE -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/computers/42/apps/17'" }, { "method": "POST", @@ -1656,7 +1656,7 @@ "auth": "permission:computers.create (jwt_required)", "params": "body (all optional): loggedinuser, lastboottime; server sets lastreporteddate to now (UTC)", "purpose": "Agent status check-in: refresh last-reported timestamp plus logged-in user and boot time", - "example": "curl -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'" + "example": "curl -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'" }, { "method": "GET", @@ -1694,7 +1694,7 @@ "purpose": "Create a measuring-tool type; reactivates a soft-deleted same-named one, 409 if an active one exists.", "auth": "jwt + permission:measuringtools.create", "params": "body JSON: name (required), description, color", - "example": "curl -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\"}'" + "example": "curl -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\"}'" }, { "method": "PUT", @@ -1702,7 +1702,7 @@ "purpose": "Update a measuring-tool type; 409 on rename collision with an existing name.", "auth": "jwt + permission:measuringtools.edit", "params": "path: type_id; body JSON: name, description, color, isactive (only keys present are applied)", - "example": "curl -X PUT 'http://localhost:5001/api/measuringtools/types/3' -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"description\":\"Updated\",\"isactive\":true}'" + "example": "curl -X PUT 'http://localhost:5001/api/measuringtools/types/3' -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"description\":\"Updated\",\"isactive\":true}'" }, { "method": "DELETE", @@ -1710,7 +1710,7 @@ "purpose": "Hard-delete a measuring-tool type; refused with 409 if any tool still references it.", "auth": "jwt + permission:measuringtools.delete", "params": "path: type_id (int)", - "example": "curl -X DELETE 'http://localhost:5001/api/measuringtools/types/3' -H 'Authorization: Bearer $TOKEN'" + "example": "curl -X DELETE 'http://localhost:5001/api/measuringtools/types/3' -H \"Authorization: Bearer $TOKEN\"" }, { "method": "GET", @@ -1742,7 +1742,7 @@ "purpose": "Create a measuring tool: one Asset core row (assettype 'measuring_tool') plus one measuringtools extension row in a single payload; audit-logged.", "auth": "jwt + permission:measuringtools.create", "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", - "example": "curl -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\"}'" + "example": "curl -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\"}'" }, { "method": "PUT", @@ -1750,7 +1750,7 @@ "purpose": "Update asset core fields and extension fields in one payload; 409 on assetnumber collision; changes audit-logged.", "auth": "jwt + permission:measuringtools.edit", "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)", - "example": "curl -X PUT 'http://localhost:5001/api/measuringtools/17' -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"locationid\":5,\"nextcalibrationdate\":\"2026-12-01\"}'" + "example": "curl -X PUT 'http://localhost:5001/api/measuringtools/17' -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"locationid\":5,\"nextcalibrationdate\":\"2026-12-01\"}'" }, { "method": "DELETE", @@ -1758,7 +1758,7 @@ "purpose": "Soft-delete a measuring tool by setting its asset isactive=false; audit-logged.", "auth": "jwt + permission:measuringtools.delete", "params": "path: tool_id (int)", - "example": "curl -X DELETE 'http://localhost:5001/api/measuringtools/17' -H 'Authorization: Bearer $TOKEN'" + "example": "curl -X DELETE 'http://localhost:5001/api/measuringtools/17' -H \"Authorization: Bearer $TOKEN\"" }, { "method": "GET", @@ -1803,7 +1803,7 @@ "purpose": "Create a machine type; reactivates a soft-deleted type of the same name instead of 409ing", "auth": "jwt + permission:machines.create", "params": "body: machinetype (required), description, icon, color", - "example": "curl -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\"}'" + "example": "curl -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\"}'" }, { "method": "PUT", @@ -1811,7 +1811,7 @@ "purpose": "Update a machine type (rename guarded by 409 on duplicate name)", "auth": "jwt + permission:machines.edit", "params": "path: type_id; body: any of machinetype, description, icon, color, isactive", - "example": "curl -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}'" + "example": "curl -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}'" }, { "method": "DELETE", @@ -1819,7 +1819,7 @@ "purpose": "Hard-delete a machine type; refused with 409 if any machine still references it", "auth": "jwt + permission:machines.delete", "params": "path: type_id (int)", - "example": "curl -X DELETE 'http://localhost:5001/api/machines/types/3' -H 'Authorization: Bearer $TOKEN'" + "example": "curl -X DELETE 'http://localhost:5001/api/machines/types/3' -H \"Authorization: Bearer $TOKEN\"" }, { "method": "GET", @@ -1851,7 +1851,7 @@ "purpose": "Create a machine (creates both core Asset row and Machine extension row); audit-logged; honors X-Import-Mode legacy timestamps via apply_import_timestamps", "auth": "jwt + permission:machines.create", "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", - "example": "curl -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}'" + "example": "curl -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}'" }, { "method": "PUT", @@ -1859,7 +1859,7 @@ "purpose": "Update machine (asset + machine fields), with per-field change tracking to AuditLog and 409 on assetnumber conflict", "auth": "jwt + permission:machines.edit", "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", - "example": "curl -X PUT 'http://localhost:5001/api/machines/42' -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"locationid\":7,\"nextmaintenancedate\":\"2026-09-01\"}'" + "example": "curl -X PUT 'http://localhost:5001/api/machines/42' -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"locationid\":7,\"nextmaintenancedate\":\"2026-09-01\"}'" }, { "method": "DELETE", @@ -1867,7 +1867,7 @@ "purpose": "Soft-delete a machine (sets asset.isactive=False, keeps Machine row linked); audit-logged", "auth": "jwt + permission:machines.delete", "params": "path: machine_id (int)", - "example": "curl -X DELETE 'http://localhost:5001/api/machines/42' -H 'Authorization: Bearer $TOKEN'" + "example": "curl -X DELETE 'http://localhost:5001/api/machines/42' -H \"Authorization: Bearer $TOKEN\"" }, { "method": "GET", @@ -1888,7 +1888,7 @@ "purpose": "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.", "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)", "params": "query: pctype (required, =scopename), phase (default 'runtime'); header: If-None-Match for 304", - "example": "curl -H 'X-API-Key: $TOKEN' 'http://localhost:5001/api/geenforce/manifest?pctype=cmm&phase=runtime'" + "example": "curl -H \"X-API-Key: $TOKEN\" 'http://localhost:5001/api/geenforce/manifest?pctype=cmm&phase=runtime'" }, { "method": "GET", @@ -1896,7 +1896,7 @@ "purpose": "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).", "auth": "api-key (geenforce.fetch service token) OR IP allowlist; resource-bound tokens get 404 for blobs not referenced by their scopes", "params": "path: sha256 (64 lowercase hex chars, 400 otherwise); header: If-None-Match (ETag = the hash)", - "example": "curl -H 'X-API-Key: $TOKEN' -o installer.exe 'http://localhost:5001/api/geenforce/payload/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'" + "example": "curl -H \"X-API-Key: $TOKEN\" -o installer.exe 'http://localhost:5001/api/geenforce/payload/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'" }, { "method": "POST", @@ -1904,7 +1904,7 @@ "purpose": "Record one PC's enforcement cycle: applied manifest version plus per-entry self-heal outcomes (installed/skipped/failed); returns reportid + status.", "auth": "api-key (managed service token with geenforce.report scope) OR IP allowlist", "params": "JSON body: hostname (required); remainder parsed by service.record_enforcement_report (scopename, phase, appliedversion, enforcerversion, status, per-entry results, counts); 400 on ValueError", - "example": "curl -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" + "example": "curl -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" }, { "method": "GET", @@ -1912,7 +1912,7 @@ "purpose": "List all imaging PC-type scopes with entry counts and current published version numbers, ordered by phase then scopename.", "auth": "jwt + permission:geenforce.manage", "params": "none", - "example": "curl -H 'Authorization: Bearer $JWT' http://localhost:5001/api/geenforce/scopes" + "example": "curl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/scopes" }, { "method": "POST", @@ -1920,7 +1920,7 @@ "purpose": "Create a new manifest scope; 400 if scopename missing, phase invalid, or scope already exists for that scopename+phase; returns 201 with scope summary.", "auth": "jwt + permission:geenforce.manage", "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')", - "example": "curl -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" + "example": "curl -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" }, { "method": "GET", @@ -1928,7 +1928,7 @@ "purpose": "Get one scope's summary plus its full draft entry list (each entry includes entryid, sortorder, curated appid/appname link, and inline-payload metadata).", "auth": "jwt + permission:geenforce.manage", "params": "path: scopeid", - "example": "curl -H 'Authorization: Bearer $JWT' http://localhost:5001/api/geenforce/scopes/3" + "example": "curl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/scopes/3" }, { "method": "PUT", @@ -1936,7 +1936,7 @@ "purpose": "Update scope metadata fields (only keys present in the body are changed); scopename and phase are immutable here.", "auth": "jwt + permission:geenforce.manage", "params": "JSON body (all optional): description, computertypeid, measuringtooltypeid, manifestversion (stringified), iscommon (bool-coerced)", - "example": "curl -X PUT -H 'Authorization: Bearer $JWT' -H 'Content-Type: application/json' -d '{\"description\":\"updated\",\"manifestversion\":\"1.1\"}' http://localhost:5001/api/geenforce/scopes/3" + "example": "curl -X PUT -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"description\":\"updated\",\"manifestversion\":\"1.1\"}' http://localhost:5001/api/geenforce/scopes/3" }, { "method": "DELETE", @@ -1944,7 +1944,7 @@ "purpose": "Delete a scope (and via cascade its entries); returns {deleted: scopeid}.", "auth": "jwt + permission:geenforce.manage", "params": "path: scopeid", - "example": "curl -X DELETE -H 'Authorization: Bearer $JWT' http://localhost:5001/api/geenforce/scopes/3" + "example": "curl -X DELETE -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/scopes/3" }, { "method": "GET", @@ -1952,7 +1952,7 @@ "purpose": "Render the DRAFT manifest JSON that a publish would freeze, for admin review before shipping.", "auth": "jwt + permission:geenforce.manage", "params": "path: scopeid", - "example": "curl -H 'Authorization: Bearer $JWT' http://localhost:5001/api/geenforce/scopes/3/preview" + "example": "curl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/scopes/3/preview" }, { "method": "GET", @@ -1960,7 +1960,7 @@ "purpose": "List the core active Applications catalog (appid + appname) for the curated entry-to-app link picker in the entry editor.", "auth": "jwt + permission:geenforce.manage", "params": "none", - "example": "curl -H 'Authorization: Bearer $JWT' http://localhost:5001/api/geenforce/applications" + "example": "curl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/applications" }, { "method": "POST", @@ -1968,7 +1968,7 @@ "purpose": "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.", "auth": "jwt + permission:geenforce.manage", "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.)", - "example": "curl -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" + "example": "curl -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" }, { "method": "PUT", @@ -1976,7 +1976,7 @@ "purpose": "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.", "auth": "jwt + permission:geenforce.manage", "params": "path: entryid; JSON body: Name (required), Type (required), optional appid, plus manifest fields", - "example": "curl -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" + "example": "curl -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" }, { "method": "DELETE", @@ -1984,7 +1984,7 @@ "purpose": "Delete a manifest entry; returns {deleted: entryid}.", "auth": "jwt + permission:geenforce.manage", "params": "path: entryid", - "example": "curl -X DELETE -H 'Authorization: Bearer $JWT' http://localhost:5001/api/geenforce/entries/17" + "example": "curl -X DELETE -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/entries/17" }, { "method": "PUT", @@ -1992,7 +1992,7 @@ "purpose": "Set entry ordering from an entryid list; 400 unless the list is exactly the set of this scope's entry ids.", "auth": "jwt + permission:geenforce.manage", "params": "path: scopeid; JSON body: order = [entryid, ...] (must match the scope's entry ids exactly)", - "example": "curl -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" + "example": "curl -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" }, { "method": "GET", @@ -2000,7 +2000,7 @@ "purpose": "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.", "auth": "jwt + permission:geenforce.manage", "params": "path: scopeid; query (all optional): pctype (defaults to scopename), subtype, hostname, machinenumber, cmmversion; phase comes from the scope", - "example": "curl -H 'Authorization: Bearer $JWT' 'http://localhost:5001/api/geenforce/scopes/3/simulate?hostname=WKSTN0042&cmmversion=2023.2'" + "example": "curl -H \"Authorization: Bearer $JWT\" 'http://localhost:5001/api/geenforce/scopes/3/simulate?hostname=WKSTN0042&cmmversion=2023.2'" }, { "method": "GET", @@ -2008,7 +2008,7 @@ "purpose": "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).", "auth": "jwt + permission:geenforce.manage", "params": "path: scopeid", - "example": "curl -H 'Authorization: Bearer $JWT' http://localhost:5001/api/geenforce/scopes/3/compliance" + "example": "curl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/scopes/3/compliance" }, { "method": "POST", @@ -2016,7 +2016,7 @@ "purpose": "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.", "auth": "jwt + permission:geenforce.publish", "params": "path: entryid; multipart/form-data: file (required)", - "example": "curl -X POST -H 'Authorization: Bearer $JWT' -F 'file=@fix.ps1' http://localhost:5001/api/geenforce/entries/17/payload" + "example": "curl -X POST -H \"Authorization: Bearer $JWT\" -F 'file=@fix.ps1' http://localhost:5001/api/geenforce/entries/17/payload" }, { "method": "GET", @@ -2024,7 +2024,7 @@ "purpose": "Download the stored inline payload bytes for an entry as an attachment (404 if the entry has no payload).", "auth": "jwt + permission:geenforce.manage", "params": "path: entryid", - "example": "curl -H 'Authorization: Bearer $JWT' -o fix.ps1 http://localhost:5001/api/geenforce/entries/17/payload" + "example": "curl -H \"Authorization: Bearer $JWT\" -o fix.ps1 http://localhost:5001/api/geenforce/entries/17/payload" }, { "method": "POST", @@ -2032,7 +2032,7 @@ "purpose": "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.", "auth": "jwt + permission:geenforce.publish", "params": "path: scopeid; JSON body (optional): notes", - "example": "curl -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" + "example": "curl -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" }, { "method": "GET", @@ -2040,7 +2040,7 @@ "purpose": "List published versions for a scope, newest first (versionnumber, iscurrent, publishedat, publishedby, notes).", "auth": "jwt + permission:geenforce.manage", "params": "path: scopeid", - "example": "curl -H 'Authorization: Bearer $JWT' http://localhost:5001/api/geenforce/scopes/3/versions" + "example": "curl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/scopes/3/versions" }, { "method": "GET", @@ -2048,7 +2048,7 @@ "purpose": "Fetch one published version's frozen manifest JSON (parsed and returned in the success envelope).", "auth": "jwt + permission:geenforce.manage", "params": "path: scopeid, versionnumber", - "example": "curl -H 'Authorization: Bearer $JWT' http://localhost:5001/api/geenforce/scopes/3/versions/4" + "example": "curl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/scopes/3/versions/4" }, { "method": "POST", @@ -2056,7 +2056,7 @@ "purpose": "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.", "auth": "jwt + permission:geenforce.publish", "params": "path: scopeid; JSON body: versionnumber (required, int)", - "example": "curl -X POST -H 'Authorization: Bearer $JWT' -H 'Content-Type: application/json' -d '{\"versionnumber\":3}' http://localhost:5001/api/geenforce/scopes/3/rollback" + "example": "curl -X POST -H \"Authorization: Bearer $JWT\" -H 'Content-Type: application/json' -d '{\"versionnumber\":3}' http://localhost:5001/api/geenforce/scopes/3/rollback" }, { "method": "POST", @@ -2064,7 +2064,7 @@ "purpose": "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.", "auth": "jwt + permission:geenforce.publish", "params": "path: scopeid; no body", - "example": "curl -X POST -H 'Authorization: Bearer $JWT' http://localhost:5001/api/geenforce/scopes/3/export-share" + "example": "curl -X POST -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/scopes/3/export-share" }, { "method": "GET", @@ -2072,7 +2072,7 @@ "purpose": "Read plugin config: the on-share export root (geenforce_share_root) and the client IP allowlist CIDRs (geenforce_allowed_cidrs).", "auth": "jwt + permission:geenforce.manage", "params": "none", - "example": "curl -H 'Authorization: Bearer $JWT' http://localhost:5001/api/geenforce/config" + "example": "curl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/config" }, { "method": "PUT", @@ -2080,7 +2080,7 @@ "purpose": "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.", "auth": "jwt + permission:geenforce.publish", "params": "JSON body (both optional): shareroot (string path), allowedcidrs (CSV/newline CIDR list)", - "example": "curl -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" + "example": "curl -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" }, { "method": "GET", @@ -2088,7 +2088,7 @@ "purpose": "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.", "auth": "jwt + permission:geenforce.manage", "params": "query (optional): hostname (ILIKE match), scopename (exact)", - "example": "curl -H 'Authorization: Bearer $JWT' 'http://localhost:5001/api/geenforce/reports?scopename=cmm'" + "example": "curl -H \"Authorization: Bearer $JWT\" 'http://localhost:5001/api/geenforce/reports?scopename=cmm'" }, { "method": "GET", @@ -2096,7 +2096,7 @@ "purpose": "One enforcement report in detail with per-entry outcomes (entryname, action, selfhealed, exitcode, message) plus applied-vs-latest version comparison.", "auth": "jwt + permission:geenforce.manage", "params": "path: reportid", - "example": "curl -H 'Authorization: Bearer $JWT' http://localhost:5001/api/geenforce/reports/42" + "example": "curl -H \"Authorization: Bearer $JWT\" http://localhost:5001/api/geenforce/reports/42" } ] }, @@ -2117,7 +2117,7 @@ "purpose": "Create a notification type incl. expiry rule and shopfloor display config", "auth": "jwt + permission:notifications.create", "params": "body: typename (required, unique), typedescription/description, typecolor/color, expirymode (none|duration|dailytime), expirydays, expiryhour, expiryminute, splitperemployee, showemployeephoto, displaystyle (standard|carousel|grid|banner)", - "example": "curl -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}'" + "example": "curl -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}'" }, { "method": "PUT, PATCH", @@ -2125,7 +2125,7 @@ "purpose": "Update a notification type (name/desc/color/isactive plus expiry and display fields)", "auth": "jwt + permission:notifications.create", "params": "body: any of typename (unique-checked), typedescription/description, typecolor/color, isactive, expirymode, expirydays, expiryhour, expiryminute, splitperemployee, showemployeephoto, displaystyle", - "example": "curl -X PATCH http://localhost:5001/api/notifications/types/3 -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"expirymode\":\"duration\",\"expirydays\":14}'" + "example": "curl -X PATCH http://localhost:5001/api/notifications/types/3 -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"expirymode\":\"duration\",\"expirydays\":14}'" }, { "method": "GET", @@ -2149,7 +2149,7 @@ "purpose": "Create a notification; endtime auto-derived from type expiry rule when omitted", "auth": "jwt + permission:notifications.create", "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", - "example": "curl -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\"}'" + "example": "curl -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\"}'" }, { "method": "PUT", @@ -2157,7 +2157,7 @@ "purpose": "Update any notification field; empty starttime resets to now, empty endtime clears it", "auth": "jwt + permission:notifications.edit", "params": "body: notification/message, notificationtypeid, businessunitid, appid, ticketnumber, link/linkurl, isactive, isshopfloor, employeesso, employeename, starttime/startdate, endtime/enddate", - "example": "curl -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}'" + "example": "curl -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}'" }, { "method": "DELETE", @@ -2165,7 +2165,7 @@ "purpose": "Soft-delete a notification (sets isactive=false, row kept)", "auth": "jwt + permission:notifications.delete", "params": "path: notification_id", - "example": "curl -X DELETE http://localhost:5001/api/notifications/42 -H 'Authorization: Bearer $TOKEN'" + "example": "curl -X DELETE http://localhost:5001/api/notifications/42 -H \"Authorization: Bearer $TOKEN\"" }, { "method": "GET", @@ -2234,7 +2234,7 @@ "purpose": "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", "auth": "jwt + permission:slides.manage", "params": "path: surface (lobby|shopfloor; else VALIDATION_ERROR)", - "example": "curl -H 'Authorization: Bearer $TOKEN' 'http://localhost:5001/api/slides/lobby'" + "example": "curl -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/slides/lobby'" }, { "method": "POST", @@ -2242,7 +2242,7 @@ "purpose": "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)", "auth": "jwt + permission:slides.manage", "params": "path: surface. multipart/form-data body: files (repeatable) or single file. Allowed extensions: .jpg .jpeg .png .gif .bmp .webp. Returns {added:[names]}", - "example": "curl -X POST -H 'Authorization: Bearer $TOKEN' -F 'files=@Slide1.png' -F 'files=@Slide2.png' 'http://localhost:5001/api/slides/lobby/upload'" + "example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" -F 'files=@Slide1.png' -F 'files=@Slide2.png' 'http://localhost:5001/api/slides/lobby/upload'" }, { "method": "POST", @@ -2250,7 +2250,7 @@ "purpose": "Save play order: each filename in the order array gets sortorder set to its index; unknown filenames silently ignored", "auth": "jwt + permission:slides.manage", "params": "path: surface. JSON body: {order: [filename, ...]}", - "example": "curl -X POST -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"order\":[\"Slide2.png\",\"Slide1.png\"]}' 'http://localhost:5001/api/slides/lobby/order'" + "example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"order\":[\"Slide2.png\",\"Slide1.png\"]}' 'http://localhost:5001/api/slides/lobby/order'" }, { "method": "POST", @@ -2258,7 +2258,7 @@ "purpose": "Delete named slides: removes file from disk (OSError swallowed) and the TvSlide row; filenames reduced to basename first; returns count of DB rows removed", "auth": "jwt + permission:slides.manage", "params": "path: surface. JSON body: {files: [filename, ...]}", - "example": "curl -X POST -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"files\":[\"Slide1.png\"]}' 'http://localhost:5001/api/slides/shopfloor/delete'" + "example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"files\":[\"Slide1.png\"]}' 'http://localhost:5001/api/slides/shopfloor/delete'" }, { "method": "PATCH", @@ -2266,7 +2266,7 @@ "purpose": "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", "auth": "jwt + permission:slides.manage", "params": "path: surface, slideid (int). JSON body: {seconds: int} (non-int -> VALIDATION_ERROR)", - "example": "curl -X PATCH -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"seconds\":15}' 'http://localhost:5001/api/slides/lobby/12'" + "example": "curl -X PATCH -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"seconds\":15}' 'http://localhost:5001/api/slides/lobby/12'" } ] }, @@ -2633,7 +2633,7 @@ "purpose": "List USB devices with checkout status (paginated); dual-mode: selfhosted tables or external cmmc_usb DB per usb_directory_mode setting", "auth": "jwt-optional", "params": "query: page, per_page, status=available|checkedout|retired, search (matches device_id or device_desc)", - "example": "curl 'http://localhost:5001/api/usb?status=available&search=kingston&page=1&per_page=25' -H 'Authorization: Bearer $JWT'" + "example": "curl 'http://localhost:5001/api/usb?status=available&search=kingston&page=1&per_page=25' -H \"Authorization: Bearer $JWT\"" }, { "method": "GET", @@ -2641,7 +2641,7 @@ "purpose": "Get one device plus its last 20 check-in/out log rows; 404 if unknown", "auth": "jwt-optional", "params": "path: device_id", - "example": "curl 'http://localhost:5001/api/usb/USB-0042' -H 'Authorization: Bearer $JWT'" + "example": "curl 'http://localhost:5001/api/usb/USB-0042' -H \"Authorization: Bearer $JWT\"" }, { "method": "POST", @@ -2649,7 +2649,7 @@ "purpose": "Create a device (starts in checked-in status); 409 on duplicate device_id", "auth": "jwt + permission:usb.create", "params": "body JSON: device_id (required), device_desc, device_owner (badge), locker_location", - "example": "curl -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\"}'" + "example": "curl -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\"}'" }, { "method": "PUT", @@ -2657,7 +2657,7 @@ "purpose": "Edit device fields (device_desc / device_owner / locker_location / status); 404 if unknown", "auth": "jwt + permission:usb.edit", "params": "path: device_id; body JSON: any of device_desc, device_owner, locker_location, status", - "example": "curl -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\"}'" + "example": "curl -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\"}'" }, { "method": "POST", @@ -2665,7 +2665,7 @@ "purpose": "Retire a device (sets status to retired); 404 if unknown", "auth": "jwt + permission:usb.edit", "params": "path: device_id; no body", - "example": "curl -X POST 'http://localhost:5001/api/usb/USB-0042/retire' -H 'Authorization: Bearer $JWT'" + "example": "curl -X POST 'http://localhost:5001/api/usb/USB-0042/retire' -H \"Authorization: Bearer $JWT\"" }, { "method": "POST", @@ -2673,7 +2673,7 @@ "purpose": "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", "auth": "jwt + permission:usb.create", "params": "path: device_id; body JSON: badge (required), locker_location (optional, also updates device)", - "example": "curl -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\"}'" + "example": "curl -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\"}'" }, { "method": "POST", @@ -2681,7 +2681,7 @@ "purpose": "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", "auth": "jwt + permission:usb.create", "params": "path: device_id; body JSON: badge (required), locker_location, sanitized (bool/1/0), scanned_viruses (bool/1/0)", - "example": "curl -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}'" + "example": "curl -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}'" }, { "method": "GET", @@ -2689,7 +2689,7 @@ "purpose": "Paginated check-in/out log for one device, newest first", "auth": "jwt-optional", "params": "path: device_id; query: page, per_page", - "example": "curl 'http://localhost:5001/api/usb/USB-0042/history?page=1&per_page=50' -H 'Authorization: Bearer $JWT'" + "example": "curl 'http://localhost:5001/api/usb/USB-0042/history?page=1&per_page=50' -H \"Authorization: Bearer $JWT\"" }, { "method": "GET", @@ -2697,7 +2697,7 @@ "purpose": "List check-out log rows (paginated), each with the device's current status joined in", "auth": "jwt-optional", "params": "query: page, per_page, active=true (only rows whose device is still checked out), badge (filter by badge_number)", - "example": "curl 'http://localhost:5001/api/usb/checkouts?active=true&badge=212345678' -H 'Authorization: Bearer $JWT'" + "example": "curl 'http://localhost:5001/api/usb/checkouts?active=true&badge=212345678' -H \"Authorization: Bearer $JWT\"" }, { "method": "GET", @@ -2705,7 +2705,7 @@ "purpose": "Latest check-out log row for every currently checked-out device (non-paginated list)", "auth": "jwt-optional", "params": "query: badge (filter by badge_number)", - "example": "curl 'http://localhost:5001/api/usb/checkouts/active?badge=212345678' -H 'Authorization: Bearer $JWT'" + "example": "curl 'http://localhost:5001/api/usb/checkouts/active?badge=212345678' -H \"Authorization: Bearer $JWT\"" } ] }, diff --git a/docs/openapi.json b/docs/openapi.json index a55c2c2..4140895 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -829,7 +829,7 @@ "core-platform" ], "summary": "Email a report's rows as an HTML table on demand; recipient defaults to the site Alert Recipients setting; intended cron", - "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```", + "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": [] @@ -1020,7 +1020,7 @@ "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```", + "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": [] @@ -1039,7 +1039,7 @@ "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```", + "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": [] @@ -1056,7 +1056,7 @@ "core-platform" ], "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```", + "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```", "security": [ { "bearerAuth": [] @@ -1075,7 +1075,7 @@ "core-platform" ], "summary": "Generic collector ingest (ADR-006): schema-validated identity field, idempotent upsert via the plugin's apply_collector_", - "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```", + "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```", "security": [ { "apiKeyAuth": [] @@ -1094,7 +1094,7 @@ "core-platform" ], "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```", + "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```", "security": [ { "bearerAuth": [] @@ -1113,7 +1113,7 @@ "core-platform" ], "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```", + "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```", "security": [ { "apiKeyAuth": [] @@ -1132,7 +1132,7 @@ "core-platform" ], "summary": "Update installed applications for one PC; only apps already in the Application table are tracked, others skipped; return", - "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```", + "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```", "security": [ { "apiKeyAuth": [] @@ -1151,7 +1151,7 @@ "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```", + "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": [ { "apiKeyAuth": [] @@ -1170,7 +1170,7 @@ "core-platform" ], "summary": "Bulk update many PCs in one call (lastreporteddate, currentuser, lastboottime per entry); returns updated/notfound/error", - "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```", + "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": [] @@ -1189,7 +1189,7 @@ "core-platform" ], "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```", + "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```", "security": [ { "apiKeyAuth": [] @@ -1208,7 +1208,7 @@ "core-platform" ], "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```", + "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```", "security": [ { "bearerAuth": [] @@ -1227,7 +1227,7 @@ "core-platform" ], "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```", + "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```", "security": [ { "bearerAuth": [] @@ -1246,7 +1246,7 @@ "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```", + "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": [] @@ -1303,7 +1303,7 @@ "core-platform" ], "summary": "Enable or disable a plugin (route changes need an app restart); 409 when unknown or a dependency conflict refuses the ch", - "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```", + "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```", "security": [ { "bearerAuth": [] @@ -1443,7 +1443,7 @@ "plugin-employees" ], "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```", + "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": [] @@ -1460,7 +1460,7 @@ "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```", + "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": [] @@ -1479,7 +1479,7 @@ "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```", + "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": [] @@ -1496,7 +1496,7 @@ "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```", + "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": [] @@ -1515,7 +1515,7 @@ "plugin-employees" ], "summary": "Bulk upsert the self-hosted directory from CSV (headers SSO,First_Name,Last_Name,Team,Role,Picture case-insensitive; pla", - "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```", + "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": [] @@ -1534,7 +1534,7 @@ "plugin-employees" ], "summary": "Upload or replace a self-hosted employee's photo (saved as photo- in instance employeephotos dir; old file wip", - "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```", + "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": [] @@ -1551,7 +1551,7 @@ "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```", + "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": [] @@ -1602,7 +1602,7 @@ "plugin-knowledgebase" ], "summary": "Create a new KB article; validates required fields and that appid (if given) exists; honors X-Import-Mode timestamp pres", - "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```", + "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```", "security": [ { "bearerAuth": [] @@ -1657,7 +1657,7 @@ "plugin-knowledgebase" ], "summary": "Update an article's shortdescription, linkurl, appid, keywords, and/or isactive; validates appid if changed; honors impo", - "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```", + "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": [] @@ -1674,7 +1674,7 @@ "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```", + "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": [] @@ -3379,7 +3379,7 @@ "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```", + "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": [] @@ -3396,7 +3396,7 @@ "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```", + "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": [] @@ -3432,7 +3432,7 @@ "plugin-computers" ], "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```", + "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```", "security": [ { "bearerAuth": [] @@ -3449,7 +3449,7 @@ "plugin-computers" ], "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```", + "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```", "security": [ { "bearerAuth": [] @@ -3485,7 +3485,7 @@ "plugin-computers" ], "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```", + "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```", "security": [ { "bearerAuth": [] @@ -3504,7 +3504,7 @@ "plugin-computers" ], "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```", + "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```", "security": [ { "bearerAuth": [] @@ -3523,7 +3523,7 @@ "plugin-computers" ], "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```", + "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```", "security": [ { "bearerAuth": [] @@ -3559,7 +3559,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -3595,7 +3595,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -3612,7 +3612,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -3686,7 +3686,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -3705,7 +3705,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -3724,7 +3724,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -3779,7 +3779,7 @@ "plugin-measuringtools" ], "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```", + "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```", "security": [ { "bearerAuth": [] @@ -3815,7 +3815,7 @@ "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```", + "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": [] @@ -3832,7 +3832,7 @@ "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```", + "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": [] @@ -3868,7 +3868,7 @@ "plugin-measuringtools" ], "summary": "Create a measuring tool: one Asset core row (assettype 'measuring_tool') plus one measuringtools extension row in a sing", - "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```", + "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": [] @@ -3904,7 +3904,7 @@ "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```", + "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": [] @@ -3921,7 +3921,7 @@ "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```", + "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": [] @@ -4014,7 +4014,7 @@ "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```", + "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": [] @@ -4050,7 +4050,7 @@ "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```", + "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": [] @@ -4067,7 +4067,7 @@ "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```", + "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": [] @@ -4103,7 +4103,7 @@ "plugin-machines" ], "summary": "Create a machine (creates both core Asset row and Machine extension row); audit-logged; honors X-Import-Mode legacy time", - "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```", + "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": [] @@ -4139,7 +4139,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4156,7 +4156,7 @@ "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```", + "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": [] @@ -4213,7 +4213,7 @@ "plugin-geenforce" ], "summary": "Serve the current PUBLISHED manifest JSON snapshot for a scope (never the live draft) to the GE-Enforce client, with ETa", - "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```", + "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```", "security": [ { "apiKeyAuth": [] @@ -4232,7 +4232,7 @@ "plugin-geenforce" ], "summary": "Download a payload blob by content hash (blob store first, then inline DB payload) so share-less PCs can pull installers", - "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```", + "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```", "security": [ { "apiKeyAuth": [] @@ -4251,7 +4251,7 @@ "plugin-geenforce" ], "summary": "Record one PC's enforcement cycle: applied manifest version plus per-entry self-heal outcomes (installed/skipped/failed)", - "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```", + "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```", "security": [ { "apiKeyAuth": [] @@ -4270,7 +4270,7 @@ "plugin-geenforce" ], "summary": "List all imaging PC-type scopes with entry counts and current published version numbers, ordered by phase then scopename", - "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4287,7 +4287,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4306,7 +4306,7 @@ "plugin-geenforce" ], "summary": "Get one scope's summary plus its full draft entry list (each entry includes entryid, sortorder, curated appid/appname li", - "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```", + "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": [] @@ -4323,7 +4323,7 @@ "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```", + "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": [] @@ -4340,7 +4340,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4359,7 +4359,7 @@ "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```", + "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": [] @@ -4378,7 +4378,7 @@ "plugin-geenforce" ], "summary": "List the core active Applications catalog (appid + appname) for the curated entry-to-app link picker in the entry editor", - "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```", + "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": [] @@ -4397,7 +4397,7 @@ "plugin-geenforce" ], "summary": "Create a manifest entry in a scope at the next sortorder; validates Name (required) and Type (must be in ENTRY_TYPES); 4", - "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```", + "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": [] @@ -4416,7 +4416,7 @@ "plugin-geenforce" ], "summary": "Replace an entry's fields from the payload (re-populates via populate_entry, re-creating the one-to-one InUseCheck); sam", - "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```", + "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": [] @@ -4433,7 +4433,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4452,7 +4452,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4471,7 +4471,7 @@ "plugin-geenforce" ], "summary": "Simulate which draft entries would apply to a given machine profile and why the rest are filtered out (PCTypes/TargetHos", - "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```", + "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": [] @@ -4490,7 +4490,7 @@ "plugin-geenforce" ], "summary": "Fleet install coverage per app-linked entry (installed/version-match counts from the computers plugin's ComputerInstalle", - "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4509,7 +4509,7 @@ "plugin-geenforce" ], "summary": "Upload an inline payload file (max 1 MB, rejects empty) for an entry and point the entry at it (stores sha256, filename,", - "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4526,7 +4526,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4545,7 +4545,7 @@ "plugin-geenforce" ], "summary": "Freeze the scope's draft into a new published version (service.publish_scope), recording the publishing user from the JW", - "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4564,7 +4564,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4583,7 +4583,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4602,7 +4602,7 @@ "plugin-geenforce" ], "summary": "Roll the scope's current published pointer back to an earlier versionnumber (service.rollback_scope); 400 with the error", - "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```", + "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": [] @@ -4621,7 +4621,7 @@ "plugin-geenforce" ], "summary": "Write the scope's current published JSON to the configured share root (geenforce_share_root setting), backing up the old", - "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```", + "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": [] @@ -4640,7 +4640,7 @@ "plugin-geenforce" ], "summary": "Read plugin config: the on-share export root (geenforce_share_root) and the client IP allowlist CIDRs (geenforce_allowed", - "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4657,7 +4657,7 @@ "plugin-geenforce" ], "summary": "Update plugin config settings; allowedcidrs is validated/normalized (comma/newline-separated CIDRs or bare IPs, 400 list", - "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4676,7 +4676,7 @@ "plugin-geenforce" ], "summary": "Latest enforcement report per PC (iscurrent rows) for the fleet compliance view: applied vs latest published version (re", - "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```", + "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": [] @@ -4695,7 +4695,7 @@ "plugin-geenforce" ], "summary": "One enforcement report in detail with per-entry outcomes (entryname, action, selfhealed, exitcode, message) plus applied", - "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4727,7 +4727,7 @@ "plugin-notifications" ], "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4746,7 +4746,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4763,7 +4763,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4795,7 +4795,7 @@ "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```", + "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": [] @@ -4827,7 +4827,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4844,7 +4844,7 @@ "plugin-notifications" ], "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4968,7 +4968,7 @@ "plugin-slides" ], "summary": "Admin list of a surface's slides (TvSlide rows ordered by sortorder,slideid, filtered to files present on disk), each wi", - "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```", + "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```", "security": [ { "bearerAuth": [] @@ -4987,7 +4987,7 @@ "plugin-slides" ], "summary": "Upload one or more slide images; non-image extensions skipped, names secure_filename'd and unique-renamed (stem_N.ext) o", - "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```", + "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": [] @@ -5006,7 +5006,7 @@ "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```", + "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": [] @@ -5025,7 +5025,7 @@ "plugin-slides" ], "summary": "Delete named slides: removes file from disk (OSError swallowed) and the TvSlide row; filenames reduced to basename first", - "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```", + "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": [] @@ -5044,7 +5044,7 @@ "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 m", - "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```", + "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```", "security": [ { "bearerAuth": [] @@ -5842,7 +5842,7 @@ "plugin-usb" ], "summary": "List USB devices with checkout status (paginated); dual-mode: selfhosted tables or external cmmc_usb DB per usb_director", - "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```", + "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```", "security": [ { "bearerAuth": [] @@ -5859,7 +5859,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -5878,7 +5878,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -5895,7 +5895,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -5914,7 +5914,7 @@ "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```", + "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": [] @@ -5933,7 +5933,7 @@ "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```", + "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": [] @@ -5952,7 +5952,7 @@ "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 cur", - "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```", + "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```", "security": [ { "bearerAuth": [] @@ -5971,7 +5971,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -5990,7 +5990,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] @@ -6009,7 +6009,7 @@ "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```", + "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```", "security": [ { "bearerAuth": [] diff --git a/scripts/site_imports/wjf/PILOT-DEPLOY.md b/scripts/site_imports/wjf/PILOT-DEPLOY.md index 76c416c..7bd2a0e 100644 --- a/scripts/site_imports/wjf/PILOT-DEPLOY.md +++ b/scripts/site_imports/wjf/PILOT-DEPLOY.md @@ -43,10 +43,12 @@ disabled. A plugin's routes only register when it is enabled at app start, and the importer needs them: ```bash -for p in computers employees machines measuringtools network notifications \ - printers slides usb warranty knowledgebase geenforce; do - flask plugin enable "$p" -done +# apply-profile installs AND enables, in dependency order, and runs each +# plugin's own migration chain. `plugin enable` alone refuses a plugin that is +# not installed yet, so on a fresh database the loop below exited 1 on every +# iteration and enabled nothing (ADR-013 records the same defect). +flask plugin apply-profile deploy/site-profile-universal.json +flask plugin upgrade-all ``` Do **not** run the setup wizard yet - the import fills the data the wizard would