[ { "surface": "core-identity", "endpoints": [ { "method": "POST", "path": "/api/auth/login", "auth": "none", "params": "body: username, password (both required)", "purpose": "Authenticate and issue JWT access+refresh tokens; per-IP fixed-window rate limit (429) and 5-strike/15-min account lockout", "example": "curl -X POST http://localhost:5001/api/auth/login -H 'Content-Type: application/json' -d '{\"username\":\"admin\",\"password\":\"secret123\"}'" }, { "method": "POST", "path": "/api/auth/refresh", "auth": "jwt (refresh token)", "params": "none; refresh token in Authorization header", "purpose": "Exchange a refresh token for a new access token (rejects inactive/deleted users)", "example": "curl -X POST http://localhost:5001/api/auth/refresh -H \"Authorization: Bearer $REFRESH_TOKEN\"" }, { "method": "GET", "path": "/api/auth/me", "auth": "jwt", "params": "none", "purpose": "Return the authenticated user's profile, roles, permissions, mustchangepassword flag", "example": "curl http://localhost:5001/api/auth/me -H \"Authorization: Bearer $TOK\"" }, { "method": "POST", "path": "/api/auth/change-password", "auth": "jwt", "params": "body: new_password (min 8, required), current_password (required unless mustchangepassword is set)", "purpose": "Self-service password change; forced first-login change skips current_password; clears lockout state", "example": "curl -X POST http://localhost:5001/api/auth/change-password -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"current_password\":\"old\",\"new_password\":\"newpass123\"}'" }, { "method": "POST", "path": "/api/auth/logout", "auth": "jwt", "params": "none", "purpose": "Logout stub for frontend token cleanup (no server-side blacklist yet)", "example": "curl -X POST http://localhost:5001/api/auth/logout -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/users", "auth": "jwt + require_role admin", "params": "none", "purpose": "List all users ordered by username", "example": "curl http://localhost:5001/api/users -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/users/", "auth": "jwt (admin or self, checked inline)", "params": "path: userid (int)", "purpose": "Get one user; non-admins may only fetch their own record (403 otherwise)", "example": "curl http://localhost:5001/api/users/7 -H \"Authorization: Bearer $TOK\"" }, { "method": "POST", "path": "/api/users", "auth": "jwt + require_role admin", "params": "body: username, email, password (required); firstname, lastname, isactive, roles[roleids], mustchangepassword (default true), sendwelcome (default true)", "purpose": "Create a user, assign roles, audit-log, and best-effort send a welcome email with the temp password", "example": "curl -X POST http://localhost:5001/api/users -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"username\":\"jdoe\",\"email\":\"jdoe@example.com\",\"password\":\"Temp1234\",\"roles\":[2]}'" }, { "method": "PUT", "path": "/api/users/", "auth": "jwt (admin or self; isactive/roles/unlock fields admin-only)", "params": "path: userid; body: email, firstname, lastname, password; admin-only: isactive, roles[roleids], unlock (bool)", "purpose": "Update a user; email uniqueness enforced; admins can also change active state, roles, and unlock the account; changes audit-logged", "example": "curl -X PUT http://localhost:5001/api/users/7 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"firstname\":\"Jane\",\"unlock\":true}'" }, { "method": "DELETE", "path": "/api/users/", "auth": "jwt + require_role admin", "params": "path: userid (cannot be your own account)", "purpose": "Delete a user; revokes their API tokens and detaches (nulls userid on) their audit-log rows first", "example": "curl -X DELETE http://localhost:5001/api/users/7 -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/users/permissions", "auth": "jwt", "params": "none", "purpose": "List assignable permissions (core + enabled plugins) both flat and grouped by category, for the role grid", "example": "curl http://localhost:5001/api/users/permissions -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/users/roles", "auth": "jwt", "params": "none", "purpose": "List all roles with description, color, user count, permission names, and isadmin flag", "example": "curl http://localhost:5001/api/users/roles -H \"Authorization: Bearer $TOK\"" }, { "method": "POST", "path": "/api/users/roles", "auth": "jwt + require_role admin", "params": "body: rolename (required), description, color, permissions[names]", "purpose": "Create a role and assign permissions by name; 409 if rolename exists", "example": "curl -X POST http://localhost:5001/api/users/roles -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"rolename\":\"viewer\",\"permissions\":[\"assets.view\"]}'" }, { "method": "PUT", "path": "/api/users/roles/", "auth": "jwt + require_role admin", "params": "path: roleid; body: description, color, permissions[names] (permissions immutable on the admin role)", "purpose": "Update a role's description/color/permissions; admin role's permission set cannot be modified; audit-logged", "example": "curl -X PUT http://localhost:5001/api/users/roles/3 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"color\":\"#0057b8\",\"permissions\":[\"assets.view\",\"assets.edit\"]}'" }, { "method": "DELETE", "path": "/api/users/roles/", "auth": "jwt + require_role admin", "params": "path: roleid", "purpose": "Delete a role; refuses for the admin role or any role still assigned to users", "example": "curl -X DELETE http://localhost:5001/api/users/roles/3 -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/apitokens", "auth": "jwt", "params": "query: all=true (admin only, lists everyone's tokens with owner info)", "purpose": "List the caller's own API tokens (never returns hashes or secrets); admins may list all", "example": "curl 'http://localhost:5001/api/apitokens?all=true' -H \"Authorization: Bearer $TOK\"" }, { "method": "POST", "path": "/api/apitokens", "auth": "jwt + require_permission apitokens.create", "params": "body: name (required), expiresat (date/datetime), scopes[permission names, ceiling = owner's permissions], resourcescopes[resource names, plugin-defined]", "purpose": "Create a personal API token for the caller; the full secret is returned once in this response and never again", "example": "curl -X POST http://localhost:5001/api/apitokens -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"name\":\"import-script\",\"scopes\":[\"assets.edit\"],\"expiresat\":\"2026-12-31\"}'" }, { "method": "PUT", "path": "/api/apitokens/", "auth": "jwt + require_permission apitokens.create (own token, or any if admin)", "params": "path: tokenid; body: name, isactive (bool), scopes[names, validated against the token OWNER's permissions], resourcescopes[names]", "purpose": "Rename, rescope, or (de)activate a token; scope ceiling is always the owner, even when an admin edits", "example": "curl -X PUT http://localhost:5001/api/apitokens/4 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"isactive\":false}'" }, { "method": "DELETE", "path": "/api/apitokens/", "auth": "jwt + require_permission apitokens.create (own token, or any if admin)", "params": "path: tokenid", "purpose": "Revoke (deactivate, not delete) a token; audit-logged", "example": "curl -X DELETE http://localhost:5001/api/apitokens/4 -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/setup/needs-admin", "auth": "none", "params": "none", "purpose": "Return {needsadmin: true} when zero users exist, so the login screen can offer first-run setup", "example": "curl http://localhost:5001/api/setup/needs-admin" }, { "method": "POST", "path": "/api/setup/create-admin", "auth": "none (only functions while zero users exist; 403 afterwards)", "params": "body: username, email, password (all required)", "purpose": "Bootstrap the very first admin account (creates the admin role if missing)", "example": "curl -X POST http://localhost:5001/api/setup/create-admin -H 'Content-Type: application/json' -d '{\"username\":\"admin\",\"email\":\"admin@example.com\",\"password\":\"ChangeMe123\"}'" }, { "method": "POST", "path": "/api/setup/seed-reference", "auth": "jwt + require_role admin", "params": "none", "purpose": "Idempotently seed core reference data, permissions, and default settings (runs the flask seed CLI routines)", "example": "curl -X POST http://localhost:5001/api/setup/seed-reference -H \"Authorization: Bearer $TOK\"" }, { "method": "POST", "path": "/api/setup/seed-starter", "auth": "jwt + require_role admin", "params": "none", "purpose": "Idempotently add a starter list of common hardware vendors (Dell, HP, Lenovo, ...)", "example": "curl -X POST http://localhost:5001/api/setup/seed-starter -H \"Authorization: Bearer $TOK\"" }, { "method": "POST", "path": "/api/setup/complete", "auth": "jwt + require_role admin", "params": "none", "purpose": "Set the setup_complete setting to true, marking the first-run wizard finished", "example": "curl -X POST http://localhost:5001/api/setup/complete -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/settings", "auth": "jwt-optional", "params": "query: category (filter)", "purpose": "List settings; unauthenticated callers see only the public allowlist (branding+map categories plus named bootstrap keys); secrets always masked as ********", "example": "curl 'http://localhost:5001/api/settings?category=branding'" }, { "method": "POST", "path": "/api/settings", "auth": "jwt + require_permission settings.edit", "params": "body: key (required), value, valuetype (default string), category (default general), description", "purpose": "Create a new setting; 409 if the key exists", "example": "curl -X POST http://localhost:5001/api/settings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"key\":\"facility_name\",\"value\":\"West Jefferson\",\"category\":\"site\"}'" }, { "method": "GET", "path": "/api/settings/", "auth": "jwt-optional", "params": "path: key", "purpose": "Get one setting; non-public keys return 404 (not 403) to unauthenticated callers; secret values masked", "example": "curl http://localhost:5001/api/settings/facility_name" }, { "method": "PUT", "path": "/api/settings/", "auth": "jwt + require_permission settings.edit", "params": "path: key; body: value (required; bool coerced to 'true'/'false'; secret mask ******** means leave unchanged)", "purpose": "Update a setting (upserts a plugin-scoped string row if the key is new); audit-logged with secrets masked; invalidates the 5-min settings cache", "example": "curl -X PUT http://localhost:5001/api/settings/facility_name -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"value\":\"West Jefferson\"}'" }, { "method": "POST", "path": "/api/settings/seed", "auth": "jwt + require_permission settings.edit", "params": "none", "purpose": "Idempotently create any missing default settings (identifier toggles, search toggles, map, SMTP, SAML, etc.)", "example": "curl -X POST http://localhost:5001/api/settings/seed -H \"Authorization: Bearer $TOK\"" }, { "method": "POST", "path": "/api/settings/test-email", "auth": "jwt + require_permission settings.edit", "params": "body: to (optional; falls back to alert_recipients setting)", "purpose": "Send a test email to verify SMTP config; always 200 with a sent flag, SMTP errors returned with credentials scrubbed", "example": "curl -X POST http://localhost:5001/api/settings/test-email -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"to\":\"me@example.com\"}'" }, { "method": "POST", "path": "/api/settings/map-blueprint", "auth": "jwt + require_role admin", "params": "multipart/form-data: file (png/jpg/jpeg/gif/webp/svg), theme=light|dark", "purpose": "Upload a floor-map blueprint image to the instance maps dir and point map_blueprint_ at its served URL", "example": "curl -X POST http://localhost:5001/api/settings/map-blueprint -H \"Authorization: Bearer $TOK\" -F 'file=@floor.png' -F 'theme=light'" }, { "method": "GET", "path": "/api/settings/map-blueprint/", "auth": "none", "params": "path: filename", "purpose": "Serve an uploaded floor-map blueprint image (public so kiosk dashboards can load it)", "example": "curl -O http://localhost:5001/api/settings/map-blueprint/blueprint-light.png" }, { "method": "POST", "path": "/api/settings/branding-logo", "auth": "jwt + require_role admin", "params": "multipart/form-data: file (map image types plus .ico), kind=site|qr|badge|favicon", "purpose": "Upload a branding logo/favicon to the instance branding dir and set the matching branding setting (site_logo/qr_logo/badge_logo/site_favicon)", "example": "curl -X POST http://localhost:5001/api/settings/branding-logo -H \"Authorization: Bearer $TOK\" -F 'file=@logo.svg' -F 'kind=site'" }, { "method": "GET", "path": "/api/settings/branding/", "auth": "none", "params": "path: filename", "purpose": "Serve an uploaded branding logo (public - kiosks and print pages read it)", "example": "curl -O http://localhost:5001/api/settings/branding/logo-site.svg" } ] }, { "surface": "core-platform", "endpoints": [ { "method": "GET", "path": "/api/reports", "purpose": "List all available reports (6 core cards plus cards contributed by enabled plugins via the get_reports hook)", "auth": "jwt-optional", "params": "none", "example": "curl http://localhost:5001/api/reports" }, { "method": "GET", "path": "/api/reports/machines-by-type", "purpose": "Machine count grouped by machine type (dualpath secondary bays collapsed when the site setting is on); 404 if machines plugin absent", "auth": "jwt-optional", "params": "businessunitid (int filter), format=json|csv", "example": "curl 'http://localhost:5001/api/reports/machines-by-type?businessunitid=2&format=csv'" }, { "method": "GET", "path": "/api/reports/assets-by-status", "purpose": "Asset count grouped by status with status colors", "auth": "jwt-optional", "params": "assettypeid, businessunitid, format=json|csv", "example": "curl 'http://localhost:5001/api/reports/assets-by-status?assettypeid=1'" }, { "method": "GET", "path": "/api/reports/kb-popularity", "purpose": "Most-clicked knowledge base articles; 503 if knowledgebase plugin absent", "auth": "jwt-optional", "params": "limit (default 20, max 100), format=json|csv", "example": "curl 'http://localhost:5001/api/reports/kb-popularity?limit=10'" }, { "method": "GET", "path": "/api/reports/software-compliance", "purpose": "Required applications vs installed per PC with compliance rate and up to 100 non-compliant PCs per app; 503 if computers plugin absent", "auth": "jwt-optional", "params": "appid (filter to one app), format=json|csv", "example": "curl 'http://localhost:5001/api/reports/software-compliance?appid=5'" }, { "method": "GET", "path": "/api/reports/asset-inventory", "purpose": "Complete asset inventory summary broken down by type, status, and location", "auth": "jwt-optional", "params": "businessunitid, locationid, format=json|csv", "example": "curl 'http://localhost:5001/api/reports/asset-inventory?locationid=3&format=csv'" }, { "method": "GET", "path": "/api/reports/pc-relationships", "purpose": "PC-to-shop-floor-machine relationships (machine number, vendor, model, hostname, IP) matched in both edge directions via raw SQL UNION", "auth": "jwt-optional", "params": "format=json|csv", "example": "curl 'http://localhost:5001/api/reports/pc-relationships?format=csv'" }, { "method": "POST", "path": "/api/reports/email", "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\"}'" }, { "method": "GET", "path": "/api/search", "purpose": "Global search across assets, applications, KB, employees, notifications, custom fields, hostnames, IPs/subnets, vendor/model/type; ServiceNOW ticket prefixes and smart redirects; results capped at 50, types filterable via search__enabled settings", "auth": "jwt-optional", "params": "q (required, 2-200 chars)", "example": "curl 'http://localhost:5001/api/search?q=tsgwp00525'" }, { "method": "GET", "path": "/api/dashboard", "purpose": "Dashboard summary: asset counts by type (machines/PCs/network/printers/measuring tools, dualpath-collapsed), counts by status, 10 most recent assets", "auth": "jwt-optional", "params": "none", "example": "curl http://localhost:5001/api/dashboard" }, { "method": "GET", "path": "/api/dashboard/summary", "purpose": "Alias route for the same dashboard summary handler as GET /api/dashboard", "auth": "jwt-optional", "params": "none", "example": "curl http://localhost:5001/api/dashboard/summary" }, { "method": "GET", "path": "/api/dashboard/stats", "purpose": "Asset counts grouped by every asset type with display category labels", "auth": "jwt-optional", "params": "none", "example": "curl http://localhost:5001/api/dashboard/stats" }, { "method": "GET", "path": "/api/dashboard/navigation", "purpose": "Sidebar navigation items: core entries merged with get_navigation_items from every enabled plugin, sorted by position", "auth": "none", "params": "none", "example": "curl http://localhost:5001/api/dashboard/navigation" }, { "method": "GET", "path": "/api/dashboard/widgets", "purpose": "Dashboard widget definitions aggregated from enabled plugins (get_dashboard_widgets hook), sorted by position", "auth": "jwt-optional", "params": "none", "example": "curl http://localhost:5001/api/dashboard/widgets" }, { "method": "GET", "path": "/api/dashboard/health", "purpose": "Health check: runs SELECT 1 against the DB, returns ok/degraded plus app version", "auth": "none", "params": "none", "example": "curl http://localhost:5001/api/dashboard/health" }, { "method": "GET", "path": "/api/dashboarddefaults/visitor-location", "purpose": "Resolve the business unit for a kiosk/lobby display by FQDN first then IP (caller IP used when ipaddress omitted); null businessunitid when unmapped", "auth": "none", "params": "fqdn (optional), ipaddress (optional, defaults to client IP)", "example": "curl 'http://localhost:5001/api/dashboarddefaults/visitor-location?fqdn=display01.wjs.geaerospace.net'" }, { "method": "GET", "path": "/api/dashboarddefaults/display-role", "purpose": "Resolve what a display PC should show at boot: role (dashboard/lobby/partskiosk), frontend path, and business unit; null role when unmapped", "auth": "none", "params": "fqdn (optional), ipaddress (optional, defaults to client IP)", "example": "curl 'http://localhost:5001/api/dashboarddefaults/display-role?ipaddress=10.1.2.3'" }, { "method": "GET", "path": "/api/dashboarddefaults", "purpose": "List all active display-to-business-unit mappings", "auth": "jwt-optional", "params": "none", "example": "curl http://localhost:5001/api/dashboarddefaults" }, { "method": "POST", "path": "/api/dashboarddefaults", "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}'" }, { "method": "PUT", "path": "/api/dashboarddefaults/", "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\"}'" }, { "method": "DELETE", "path": "/api/dashboarddefaults/", "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'" }, { "method": "POST", "path": "/api/collector/", "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\":\"tsgwp00525\",\"serialnumber\":\"ABC123\"}'" }, { "method": "GET", "path": "/api/collector/_schemas", "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'" }, { "method": "POST", "path": "/api/collector/pc", "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\":\"tsgwp00525\",\"currentuser\":\"212345678\"}'" }, { "method": "POST", "path": "/api/collector/apps", "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\":\"tsgwp00525\",\"apps\":[{\"appname\":\"PC-DMIS\",\"version\":\"2023.2\"}]}'" }, { "method": "POST", "path": "/api/collector/heartbeat", "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\"]}'" }, { "method": "POST", "path": "/api/collector/bulk", "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\"}]}'" }, { "method": "GET", "path": "/api/collector/status", "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'" }, { "method": "GET", "path": "/api/auditlogs", "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'" }, { "method": "GET", "path": "/api/auditlogs/entity//", "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'" }, { "method": "GET", "path": "/api/auditlogs/stats", "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'" }, { "method": "GET", "path": "/api/plugins", "purpose": "List all discovered plugins (enabled or not) with metadata plus the framework contract version", "auth": "jwt-optional", "params": "none", "example": "curl http://localhost:5001/api/plugins" }, { "method": "GET", "path": "/api/plugins/enabled", "purpose": "Flat sorted array of enabled plugin names (registry read, no DB); deliberately anonymous so kiosk routes can gate plugin-owned frontend routes", "auth": "none (jwt-optional decorator, no claims used)", "params": "none", "example": "curl http://localhost:5001/api/plugins/enabled" }, { "method": "PUT", "path": "/api/plugins/", "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}'" }, { "method": "GET", "path": "/api/pluginui/settings-cards", "purpose": "Merge enabled plugins' settings-catalog cards (get_settings_cards hook) sorted by position for the settings rail/overview", "auth": "jwt-optional", "params": "none", "example": "curl http://localhost:5001/api/pluginui/settings-cards" }, { "method": "GET", "path": "/api/pluginui/asset-panels", "purpose": "Asset-detail panels from enabled plugins (get_asset_panels hook) matching one asset's type ('*' wildcard supported), sorted by position", "auth": "jwt-optional", "params": "assetid (int, required); 400 without it, 404 if asset missing", "example": "curl 'http://localhost:5001/api/pluginui/asset-panels?assetid=42'" }, { "method": "GET", "path": "/api/pluginui/map-overlays", "purpose": "Merge enabled plugins' map overlay declarations (get_map_overlays hook), sorted by position", "auth": "jwt-optional", "params": "none", "example": "curl http://localhost:5001/api/pluginui/map-overlays" }, { "method": "GET", "path": "/api/pluginui/asset-presentation", "purpose": "Merge enabled plugins' asset-type presentation entries (icon + detail route per type) used by search rows and cross-links", "auth": "jwt-optional", "params": "none", "example": "curl http://localhost:5001/api/pluginui/asset-presentation" } ] }, { "surface": "plugin-employees", "endpoints": [ { "method": "GET", "path": "/api/employees/search", "purpose": "Search employees by first name, last name, or SSO substring (self-hosted table or external HR DB depending on employee_directory_mode setting).", "auth": "none", "params": "q (query string, min 2 chars, required), limit (max results, default 10, capped 50)", "example": "curl 'http://localhost:5001/api/employees/search?q=smith&limit=5'" }, { "method": "GET", "path": "/api/employees/lookup/", "purpose": "Look up a single employee by numeric SSO; returns directory fields plus resolved photourl.", "auth": "none", "params": "sso (path, numeric)", "example": "curl 'http://localhost:5001/api/employees/lookup/210009518'" }, { "method": "GET", "path": "/api/employees/lookup", "purpose": "Bulk lookup of multiple employees by SSO list; returns employees array plus a joined names string.", "auth": "none", "params": "sso (query, comma-separated numeric SSOs, at least one required)", "example": "curl 'http://localhost:5001/api/employees/lookup?sso=210009518,210001234'" }, { "method": "GET", "path": "/api/employees/directory", "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'" }, { "method": "POST", "path": "/api/employees/directory", "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'" }, { "method": "PUT", "path": "/api/employees/directory/", "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'" }, { "method": "DELETE", "path": "/api/employees/directory/", "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'" }, { "method": "POST", "path": "/api/employees/directory/import", "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'" }, { "method": "POST", "path": "/api/employees//photo", "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'" }, { "method": "GET", "path": "/api/employees/photo/", "purpose": "Serve an uploaded employee photo file from the instance employeephotos dir (public so kiosk recognition cards can read it).", "auth": "none", "params": "filename (path, e.g. photo-210009518.jpg)", "example": "curl 'http://localhost:5001/api/employees/photo/photo-210009518.jpg'" }, { "method": "DELETE", "path": "/api/employees//photo", "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'" } ] }, { "endpoints": [ { "method": "GET", "path": "/api/knowledgebase", "purpose": "List active KB articles with search, topic filter, natural-key lookup, sorting, and pagination; each item includes its linked application (appid/appname) or null.", "auth": "jwt-optional", "params": "query: page, per_page, search (ILIKE on shortdescription/keywords/application name), appid (int filter), linkurl (exact natural-key match), shortdescription (exact match), sort (clicks|topic|description|lastupdated, default clicks), order (asc|desc, default desc)", "example": "curl 'http://localhost:5001/api/knowledgebase?search=printer&sort=clicks&order=desc&page=1&per_page=20'" }, { "method": "GET", "path": "/api/knowledgebase/stats", "purpose": "Return aggregate stats for active articles: totalclicks (sum of clicks) and totalarticles (count).", "auth": "jwt-optional", "params": "none", "example": "curl 'http://localhost:5001/api/knowledgebase/stats'" }, { "method": "GET", "path": "/api/knowledgebase/", "purpose": "Fetch a single active article by id with its application (appid/appname) or null; 404 if missing or inactive.", "auth": "jwt-optional", "params": "path: link_id (int)", "example": "curl 'http://localhost:5001/api/knowledgebase/42'" }, { "method": "POST", "path": "/api/knowledgebase//click", "purpose": "Increment the article's click counter and return the target linkurl plus new click count (used for redirect tracking); 404 if missing or inactive.", "auth": "jwt-optional", "params": "path: link_id (int); no body", "example": "curl -X POST 'http://localhost:5001/api/knowledgebase/42/click'" }, { "method": "POST", "path": "/api/knowledgebase", "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\"}'" }, { "method": "PUT", "path": "/api/knowledgebase/", "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}'" }, { "method": "DELETE", "path": "/api/knowledgebase/", "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'" } ], "surface": "plugin-knowledgebase" }, { "surface": "core-catalog", "endpoints": [ { "method": "GET", "path": "/api/assets/types", "purpose": "List asset types (paginated, active-only by default)", "auth": "jwt-optional", "params": "page, per_page, active=false to include inactive", "example": "curl http://localhost:5001/api/assets/types?active=false" }, { "method": "GET", "path": "/api/assets/types/", "purpose": "Get one asset type", "auth": "jwt-optional", "params": "path: type_id", "example": "curl http://localhost:5001/api/assets/types/1" }, { "method": "POST", "path": "/api/assets/types", "purpose": "Create asset type (409 on duplicate name)", "auth": "permission:assets.create", "params": "body: assettype (req), pluginname, tablename, description, icon, color", "example": "curl -X POST http://localhost:5001/api/assets/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"assettype\":\"Robot\",\"icon\":\"mdi-robot\"}'" }, { "method": "PUT", "path": "/api/assets/types/", "purpose": "Update asset type display fields only (name/plugin/table are structural, not editable)", "auth": "permission:assets.edit", "params": "body: description, icon, color, isactive", "example": "curl -X PUT http://localhost:5001/api/assets/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"color\":\"#ff0000\"}'" }, { "method": "GET", "path": "/api/assets/statuses", "purpose": "List asset statuses (paginated, active-only by default)", "auth": "jwt-optional", "params": "page, per_page, active=false", "example": "curl http://localhost:5001/api/assets/statuses" }, { "method": "GET", "path": "/api/assets/statuses/", "purpose": "Get one asset status", "auth": "jwt-optional", "params": "path: status_id", "example": "curl http://localhost:5001/api/assets/statuses/1" }, { "method": "POST", "path": "/api/assets/statuses", "purpose": "Create asset status (409 on duplicate)", "auth": "permission:assets.create", "params": "body: status (req), description, color", "example": "curl -X POST http://localhost:5001/api/assets/statuses -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"status\":\"In Repair\",\"color\":\"#f90\"}'" }, { "method": "PUT", "path": "/api/assets/statuses/", "purpose": "Update asset status (rename conflict-checked)", "auth": "permission:assets.edit", "params": "body: status, description, color, isactive", "example": "curl -X PUT http://localhost:5001/api/assets/statuses/2 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"isactive\":false}'" }, { "method": "DELETE", "path": "/api/assets/statuses/", "purpose": "Hard-delete asset status; 409 if any asset still uses it", "auth": "permission:assets.delete", "params": "path: status_id", "example": "curl -X DELETE http://localhost:5001/api/assets/statuses/9 -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/assets/relationshiptypes", "purpose": "List relationship types incl. read-only propagatesthrough rails", "auth": "jwt-optional", "params": "none", "example": "curl http://localhost:5001/api/assets/relationshiptypes" }, { "method": "POST", "path": "/api/assets/relationshiptypes", "purpose": "Create relationship type (409 on duplicate)", "auth": "permission:assets.create", "params": "body: relationshiptype (req), description, color, isdirectional (default true)", "example": "curl -X POST http://localhost:5001/api/assets/relationshiptypes -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"relationshiptype\":\"controls\",\"isdirectional\":true}'" }, { "method": "PUT", "path": "/api/assets/relationshiptypes/", "purpose": "Update relationship type (rename conflict-checked)", "auth": "permission:assets.edit", "params": "body: relationshiptype, description, color, isdirectional", "example": "curl -X PUT http://localhost:5001/api/assets/relationshiptypes/3 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"color\":\"#00f\"}'" }, { "method": "DELETE", "path": "/api/assets/relationshiptypes/", "purpose": "Hard-delete relationship type; 409 while relationships use it", "auth": "permission:assets.delete", "params": "path: type_id", "example": "curl -X DELETE http://localhost:5001/api/assets/relationshiptypes/3 -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/assets", "purpose": "List assets with filtering, search, sorting, pagination", "auth": "jwt-optional", "params": "page, per_page, active, search (assetnumber/name/serialnumber ilike), type (name), typeid|type_id, statusid|status_id, locationid|location_id, businessunitid|businessunit_id, sort (assetnumber|name|createddate|modifieddate), dir (asc|desc), include_type_data", "example": "curl 'http://localhost:5001/api/assets?type=machine&search=205&include_type_data=true'" }, { "method": "GET", "path": "/api/assets/", "purpose": "Get one asset with full details", "auth": "jwt-optional", "params": "include_type_data (default true)", "example": "curl http://localhost:5001/api/assets/42" }, { "method": "POST", "path": "/api/assets", "purpose": "Create asset (duplicate assetnumber 409, assettypeid validated); honors X-Import-Mode timestamps", "auth": "permission:assets.create", "params": "body: assetnumber (req), assettypeid (req), name, serialnumber, statusid (default 1), locationid, businessunitid, mapx, mapy, notes", "example": "curl -X POST http://localhost:5001/api/assets -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"assetnumber\":\"0205\",\"assettypeid\":1,\"name\":\"Grinder 5\"}'" }, { "method": "PUT", "path": "/api/assets/", "purpose": "Update asset (allowed fields incl. isactive); honors X-Import-Mode", "auth": "permission:assets.edit", "params": "body: assetnumber, name, serialnumber, assettypeid, statusid, locationid, businessunitid, mapx, mapy, notes, isactive", "example": "curl -X PUT http://localhost:5001/api/assets/42 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"locationid\":3}'" }, { "method": "DELETE", "path": "/api/assets/", "purpose": "Soft-delete asset (isactive=false)", "auth": "permission:assets.delete", "params": "path: asset_id", "example": "curl -X DELETE http://localhost:5001/api/assets/42 -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/assets/lookup/", "purpose": "Look up an active asset by asset number (returns full type data)", "auth": "jwt-optional", "params": "path: assetnumber (string)", "example": "curl http://localhost:5001/api/assets/lookup/0205" }, { "method": "GET", "path": "/api/assets//relationships", "purpose": "Get outgoing + incoming active relationships for an asset with partner asset dicts", "auth": "jwt-optional", "params": "path: asset_id", "example": "curl http://localhost:5001/api/assets/42/relationships" }, { "method": "POST", "path": "/api/assets/relationships", "purpose": "Create relationship, then fan out across symmetric propagation rails (Dualpath); response carries propagated + propagatedcount", "auth": "permission:assets.create", "params": "body: sourceassetid (req), targetassetid (req), relationshiptypeid (req), notes; X-Import-Mode honored", "example": "curl -X POST http://localhost:5001/api/assets/relationships -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"sourceassetid\":10,\"targetassetid\":42,\"relationshiptypeid\":1}'" }, { "method": "DELETE", "path": "/api/assets/relationships/", "purpose": "Soft-delete one relationship row (no cascade to propagated partner rows)", "auth": "permission:assets.delete", "params": "path: rel_id", "example": "curl -X DELETE http://localhost:5001/api/assets/relationships/7 -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/assets/map", "purpose": "Unified floor-map payload: all mapped assets (with type data, primary IP, dualpath collapse) plus filter option lists", "auth": "jwt-optional", "params": "assettype (name), subtype (id, per-type), businessunitid, statusid, locationid, search", "example": "curl 'http://localhost:5001/api/assets/map?assettype=machine&statusid=1'" }, { "method": "GET", "path": "/api/assets//communications", "purpose": "List active communications (IPs etc.) for an asset with comtype_name", "auth": "jwt-optional", "params": "path: asset_id", "example": "curl http://localhost:5001/api/assets/42/communications" }, { "method": "GET", "path": "/api/locations/types", "purpose": "List location types", "auth": "jwt-optional", "params": "active=false includes inactive", "example": "curl http://localhost:5001/api/locations/types" }, { "method": "POST", "path": "/api/locations/types", "purpose": "Create location type; reactivates a soft-deleted same-name type instead of 409", "auth": "admin", "params": "body: locationtype (req), description, color", "example": "curl -X POST http://localhost:5001/api/locations/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"locationtype\":\"Operation\"}'" }, { "method": "PUT", "path": "/api/locations/types/", "purpose": "Update location type (rename conflict-checked)", "auth": "admin", "params": "body: locationtype, description, color, isactive", "example": "curl -X PUT http://localhost:5001/api/locations/types/2 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"color\":\"#0a0\"}'" }, { "method": "DELETE", "path": "/api/locations/types/", "purpose": "Hard-delete location type; 409 while locations use it", "auth": "admin", "params": "path: type_id", "example": "curl -X DELETE http://localhost:5001/api/locations/types/2 -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/locations", "purpose": "List locations (paginated); exact locationname lookup for idempotent import", "auth": "jwt-optional", "params": "page, per_page, active, locationname (exact), search (name/building ilike)", "example": "curl 'http://localhost:5001/api/locations?locationname=Building%201'" }, { "method": "GET", "path": "/api/locations/", "purpose": "Get one location", "auth": "jwt-optional", "params": "path: location_id", "example": "curl http://localhost:5001/api/locations/3" }, { "method": "POST", "path": "/api/locations", "purpose": "Create location (409 on duplicate name); honors X-Import-Mode", "auth": "admin", "params": "body: locationname (req), building, floor, room, description, locationtypeid, parentlocationid, mapimage, mapwidth, mapheight", "example": "curl -X POST http://localhost:5001/api/locations -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"locationname\":\"Cell 12\",\"building\":\"B1\"}'" }, { "method": "PUT", "path": "/api/locations/", "purpose": "Update location (rename conflict-checked); honors X-Import-Mode", "auth": "admin", "params": "body: locationname, building, floor, room, description, locationtypeid, parentlocationid, mapimage, mapwidth, mapheight, isactive", "example": "curl -X PUT http://localhost:5001/api/locations/3 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"room\":\"104\"}'" }, { "method": "DELETE", "path": "/api/locations/", "purpose": "Soft-delete location", "auth": "admin", "params": "path: location_id", "example": "curl -X DELETE http://localhost:5001/api/locations/3 -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/vendors", "purpose": "List vendors (paginated); exact vendor lookup for idempotent import", "auth": "jwt-optional", "params": "page, per_page, active, vendor (exact), search (ilike)", "example": "curl 'http://localhost:5001/api/vendors?search=fanuc'" }, { "method": "GET", "path": "/api/vendors/", "purpose": "Get one vendor", "auth": "jwt-optional", "params": "path: vendor_id", "example": "curl http://localhost:5001/api/vendors/5" }, { "method": "POST", "path": "/api/vendors", "purpose": "Create vendor (409 on duplicate); honors X-Import-Mode", "auth": "admin", "params": "body: vendor (req), description, website, supportphone, supportemail, notes", "example": "curl -X POST http://localhost:5001/api/vendors -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"vendor\":\"Fanuc\"}'" }, { "method": "PUT", "path": "/api/vendors/", "purpose": "Update vendor (rename conflict-checked); honors X-Import-Mode", "auth": "admin", "params": "body: vendor, description, website, supportphone, supportemail, notes, isactive", "example": "curl -X PUT http://localhost:5001/api/vendors/5 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"website\":\"https://fanuc.com\"}'" }, { "method": "DELETE", "path": "/api/vendors/", "purpose": "Soft-delete vendor", "auth": "admin", "params": "path: vendor_id", "example": "curl -X DELETE http://localhost:5001/api/vendors/5 -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/models", "purpose": "List vendor catalog models (paginated) with flattened vendor/modeltype names; exact modelnumber+vendor lookup for import", "auth": "jwt-optional", "params": "page, per_page, active, vendor (id), modeltype (id), modelnumber (exact), search (ilike)", "example": "curl 'http://localhost:5001/api/models?vendor=5&search=30i'" }, { "method": "GET", "path": "/api/models/", "purpose": "Get one model with nested vendor + modeltype dicts", "auth": "jwt-optional", "params": "path: model_id", "example": "curl http://localhost:5001/api/models/12" }, { "method": "POST", "path": "/api/models", "purpose": "Create model (409 on duplicate modelnumber+vendorid); honors X-Import-Mode", "auth": "admin", "params": "body: modelnumber (req), vendorid, modeltypeid, description, imageurl, documentationurl, notes", "example": "curl -X POST http://localhost:5001/api/models -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"modelnumber\":\"R-30iB\",\"vendorid\":5}'" }, { "method": "PUT", "path": "/api/models/", "purpose": "Update model; honors X-Import-Mode", "auth": "admin", "params": "body: modelnumber, vendorid, modeltypeid, description, imageurl, documentationurl, notes, isactive", "example": "curl -X PUT http://localhost:5001/api/models/12 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"notes\":\"EOL 2027\"}'" }, { "method": "DELETE", "path": "/api/models/", "purpose": "Soft-delete model", "auth": "admin", "params": "path: model_id", "example": "curl -X DELETE http://localhost:5001/api/models/12 -H \"Authorization: Bearer $TOK\"" }, { "method": "POST", "path": "/api/models//image", "purpose": "Upload/replace model photo (saved as instance/modelimages/model-, one per model); sets imageurl", "auth": "admin", "params": "multipart/form-data: file= (.png/.jpg/.jpeg/.gif/.webp/.svg)", "example": "curl -X POST http://localhost:5001/api/models/12/image -H \"Authorization: Bearer $TOK\" -F file=@robot.jpg" }, { "method": "GET", "path": "/api/models/image/", "purpose": "Serve an uploaded model image (deliberately public - asset detail pages read it without auth)", "auth": "none", "params": "path: filename", "example": "curl http://localhost:5001/api/models/image/model-12.jpg" }, { "method": "DELETE", "path": "/api/models//image", "purpose": "Clear imageurl and delete the uploaded file only if it lives under /api/models/image/ (external URLs untouched)", "auth": "admin", "params": "path: model_id", "example": "curl -X DELETE http://localhost:5001/api/models/12/image -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/modeltypes", "purpose": "List model types (types the vendor MODELS catalog, not machines); exact modeltype lookup for import", "auth": "jwt-optional", "params": "page, per_page, active, category, modeltype (exact), search (ilike)", "example": "curl 'http://localhost:5001/api/modeltypes?category=Equipment'" }, { "method": "GET", "path": "/api/modeltypes/", "purpose": "Get one model type", "auth": "jwt-optional", "params": "path: type_id", "example": "curl http://localhost:5001/api/modeltypes/2" }, { "method": "POST", "path": "/api/modeltypes", "purpose": "Create model type (409 on duplicate); category defaults to Equipment; honors X-Import-Mode", "auth": "admin", "params": "body: modeltype (req), category, description, icon", "example": "curl -X POST http://localhost:5001/api/modeltypes -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"modeltype\":\"Controller\"}'" }, { "method": "PUT", "path": "/api/modeltypes/", "purpose": "Update model type (rename conflict-checked); honors X-Import-Mode", "auth": "admin", "params": "body: modeltype, category, description, icon, isactive", "example": "curl -X PUT http://localhost:5001/api/modeltypes/2 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"icon\":\"mdi-chip\"}'" }, { "method": "DELETE", "path": "/api/modeltypes/", "purpose": "Soft-delete model type; 409 while models use it", "auth": "admin", "params": "path: type_id", "example": "curl -X DELETE http://localhost:5001/api/modeltypes/2 -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/businessunits", "purpose": "List business units (paginated); exact businessunit lookup for import", "auth": "jwt-optional", "params": "page, per_page, active, businessunit (exact), search (name/code ilike)", "example": "curl http://localhost:5001/api/businessunits" }, { "method": "GET", "path": "/api/businessunits/", "purpose": "Get one business unit with parent + children", "auth": "jwt-optional", "params": "path: bu_id", "example": "curl http://localhost:5001/api/businessunits/1" }, { "method": "POST", "path": "/api/businessunits", "purpose": "Create business unit (409 on duplicate); honors X-Import-Mode", "auth": "admin", "params": "body: businessunit (req), code, description, parentid", "example": "curl -X POST http://localhost:5001/api/businessunits -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"businessunit\":\"Blades\",\"code\":\"BLD\"}'" }, { "method": "PUT", "path": "/api/businessunits/", "purpose": "Update business unit (rename conflict-checked); honors X-Import-Mode", "auth": "admin", "params": "body: businessunit, code, description, parentid, isactive", "example": "curl -X PUT http://localhost:5001/api/businessunits/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"code\":\"BL\"}'" }, { "method": "DELETE", "path": "/api/businessunits/", "purpose": "Soft-delete business unit", "auth": "admin", "params": "path: bu_id", "example": "curl -X DELETE http://localhost:5001/api/businessunits/1 -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/operatingsystems", "purpose": "List operating systems (paginated); exact osname/osversion lookup for import", "auth": "jwt-optional", "params": "page, per_page, active, osname (exact), osversion (exact), search (osname ilike)", "example": "curl 'http://localhost:5001/api/operatingsystems?osname=Windows%2011'" }, { "method": "GET", "path": "/api/operatingsystems/", "purpose": "Get one operating system", "auth": "jwt-optional", "params": "path: os_id", "example": "curl http://localhost:5001/api/operatingsystems/4" }, { "method": "POST", "path": "/api/operatingsystems", "purpose": "Create OS (409 on duplicate osname+osversion); honors X-Import-Mode", "auth": "admin", "params": "body: osname (req), osversion, architecture, endoflife", "example": "curl -X POST http://localhost:5001/api/operatingsystems -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"osname\":\"Windows 11\",\"osversion\":\"24H2\"}'" }, { "method": "PUT", "path": "/api/operatingsystems/", "purpose": "Update OS; honors X-Import-Mode", "auth": "admin", "params": "body: osname, osversion, architecture, endoflife, isactive", "example": "curl -X PUT http://localhost:5001/api/operatingsystems/4 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"endoflife\":\"2031-10-14\"}'" }, { "method": "DELETE", "path": "/api/operatingsystems/", "purpose": "Soft-delete OS", "auth": "admin", "params": "path: os_id", "example": "curl -X DELETE http://localhost:5001/api/operatingsystems/4 -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/customfields", "purpose": "List custom-field definitions, ordered by sortorder", "auth": "jwt-optional", "params": "assettypeid (int), active=false includes inactive", "example": "curl 'http://localhost:5001/api/customfields?assettypeid=1'" }, { "method": "POST", "path": "/api/customfields", "purpose": "Create field definition; fieldkey auto-slugged from label; reactivates a soft-deleted same-key field instead of 409", "auth": "admin", "params": "body: assettypeid (req), label (req), datatype (one of CUSTOM_FIELD_DATATYPES, default text), fieldkey, options (list or newline/comma text), showondetail, showonform, searchable, sortorder", "example": "curl -X POST http://localhost:5001/api/customfields -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"assettypeid\":1,\"label\":\"Coolant Type\",\"datatype\":\"select\",\"options\":[\"Oil\",\"Water\"]}'" }, { "method": "PUT", "path": "/api/customfields/", "purpose": "Update field definition (label/datatype/options/flags/sortorder)", "auth": "admin", "params": "body: label, datatype, options, showondetail, showonform, isactive, searchable, sortorder", "example": "curl -X PUT http://localhost:5001/api/customfields/7 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"searchable\":true}'" }, { "method": "DELETE", "path": "/api/customfields/", "purpose": "Hard-delete field definition AND all stored values for it", "auth": "admin", "params": "path: fieldid", "example": "curl -X DELETE http://localhost:5001/api/customfields/7 -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/customfields/asset/", "purpose": "Active field defs for the asset's type merged with the asset's stored values", "auth": "jwt-optional", "params": "path: assetid", "example": "curl http://localhost:5001/api/customfields/asset/42" }, { "method": "PUT", "path": "/api/customfields/asset/", "purpose": "Upsert per-asset values; empty string clears a value; only fields of the asset's type accepted", "auth": "admin", "params": "body: {values: {fieldid: value, ...}}", "example": "curl -X PUT http://localhost:5001/api/customfields/asset/42 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"values\":{\"7\":\"Oil\"}}'" }, { "method": "GET", "path": "/api/supportteams", "purpose": "List support teams with contacts; exact teamname lookup for import (not paginated)", "auth": "jwt-optional", "params": "active, teamname (exact)", "example": "curl http://localhost:5001/api/supportteams" }, { "method": "GET", "path": "/api/supportteams/", "purpose": "Get one support team with contacts", "auth": "jwt-optional", "params": "path: team_id", "example": "curl http://localhost:5001/api/supportteams/2" }, { "method": "POST", "path": "/api/supportteams", "purpose": "Create support team (409 on duplicate); audit-logged; honors X-Import-Mode", "auth": "admin", "params": "body: teamname (req), teamurl, webhookurl, isactive", "example": "curl -X POST http://localhost:5001/api/supportteams -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"teamname\":\"CNC Support\"}'" }, { "method": "PUT", "path": "/api/supportteams/", "purpose": "Update support team (rename conflict-checked); audit-logged; honors X-Import-Mode", "auth": "admin", "params": "body: teamname, teamurl, webhookurl, isactive", "example": "curl -X PUT http://localhost:5001/api/supportteams/2 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"webhookurl\":\"https://hooks/x\"}'" }, { "method": "DELETE", "path": "/api/supportteams/", "purpose": "Hard-delete team (cascade removes contacts); 409 while applications reference it; audit-logged", "auth": "admin", "params": "path: team_id", "example": "curl -X DELETE http://localhost:5001/api/supportteams/2 -H \"Authorization: Bearer $TOK\"" }, { "method": "POST", "path": "/api/supportteams//contacts", "purpose": "Add contact to a team; audit-logged; honors X-Import-Mode", "auth": "admin", "params": "body: name (req), sso, sortorder, isactive", "example": "curl -X POST http://localhost:5001/api/supportteams/2/contacts -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"name\":\"Jane Doe\",\"sso\":\"212345678\"}'" }, { "method": "PUT", "path": "/api/supportteams//contacts/", "purpose": "Update a team contact; audit-logged; honors X-Import-Mode", "auth": "admin", "params": "body: name, sso, sortorder, isactive", "example": "curl -X PUT http://localhost:5001/api/supportteams/2/contacts/9 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"sortorder\":1}'" }, { "method": "DELETE", "path": "/api/supportteams//contacts/", "purpose": "Hard-delete a team contact; audit-logged", "auth": "admin", "params": "path: team_id, contact_id", "example": "curl -X DELETE http://localhost:5001/api/supportteams/2/contacts/9 -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/applications", "purpose": "List applications (paginated) with installedcount; hidden apps excluded unless showhidden=true; exact appname lookup for import", "auth": "jwt-optional", "params": "page, per_page, active, showhidden, installable (true/false), appname (exact), search (name/description ilike)", "example": "curl 'http://localhost:5001/api/applications?installable=true'" }, { "method": "GET", "path": "/api/applications/", "purpose": "Get one application with active versions, installedcount, and linked KB articles", "auth": "jwt-optional", "params": "path: app_id", "example": "curl http://localhost:5001/api/applications/15" }, { "method": "POST", "path": "/api/applications", "purpose": "Create application (409 on duplicate name); audit-logged; honors X-Import-Mode", "auth": "permission:applications.create", "params": "body: appname (req), appdescription, supportteamid, isinstallable, applicationnotes, installpath, applicationlink, documentationpath, ishidden, isprinter, islicenced, isrequired, image", "example": "curl -X POST http://localhost:5001/api/applications -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"appname\":\"PC-DMIS\",\"isinstallable\":true}'" }, { "method": "PUT", "path": "/api/applications/", "purpose": "Update application (rename conflict-checked); field-level change diff audit-logged; honors X-Import-Mode", "auth": "permission:applications.edit", "params": "body: any of appname, appdescription, supportteamid, isinstallable, applicationnotes, installpath, applicationlink, documentationpath, ishidden, isprinter, islicenced, isrequired, image, isactive", "example": "curl -X PUT http://localhost:5001/api/applications/15 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"supportteamid\":2}'" }, { "method": "DELETE", "path": "/api/applications/", "purpose": "Soft-delete application; audit-logged", "auth": "permission:applications.delete", "params": "path: app_id", "example": "curl -X DELETE http://localhost:5001/api/applications/15 -H \"Authorization: Bearer $TOK\"" }, { "method": "GET", "path": "/api/applications//versions", "purpose": "List active versions of an application (desc)", "auth": "jwt-optional", "params": "path: app_id", "example": "curl http://localhost:5001/api/applications/15/versions" }, { "method": "POST", "path": "/api/applications//versions", "purpose": "Create app version (409 on duplicate version per app); honors X-Import-Mode for legacy dates", "auth": "permission:applications.create", "params": "body: version (req), releasedate, notes", "example": "curl -X POST http://localhost:5001/api/applications/15/versions -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"version\":\"2024.2\"}'" }, { "method": "GET", "path": "/api/applications//installed", "purpose": "List computers with this app installed (503 if computers plugin absent)", "auth": "jwt-optional", "params": "path: app_id", "example": "curl http://localhost:5001/api/applications/15/installed" }, { "method": "GET", "path": "/api/applications/machines/", "purpose": "List apps installed on a computer (machine_id is a computerid; 503 without computers plugin)", "auth": "jwt-optional", "params": "path: machine_id", "example": "curl http://localhost:5001/api/applications/machines/8" }, { "method": "POST", "path": "/api/applications/machines/", "purpose": "Install an app on a computer; reactivates a prior soft-deleted install; 409 if already installed", "auth": "permission:applications.create", "params": "body: appid (req), appversionid", "example": "curl -X POST http://localhost:5001/api/applications/machines/8 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"appid\":15}'" }, { "method": "DELETE", "path": "/api/applications/machines//", "purpose": "Uninstall (soft-delete install row) an app from a computer", "auth": "permission:applications.delete", "params": "path: machine_id, app_id", "example": "curl -X DELETE http://localhost:5001/api/applications/machines/8/15 -H \"Authorization: Bearer $TOK\"" }, { "method": "PUT", "path": "/api/applications/machines//", "purpose": "Update an installed-app row (change appversionid)", "auth": "permission:applications.edit", "params": "body: appversionid", "example": "curl -X PUT http://localhost:5001/api/applications/machines/8/15 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"appversionid\":3}'" } ] }, { "endpoints": [ { "method": "GET", "path": "/api/computers/types", "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'" }, { "method": "GET", "path": "/api/computers/types/", "auth": "jwt-optional", "params": "type_id in path", "purpose": "Get a single computer type by ID", "example": "curl 'http://localhost:5001/api/computers/types/3'" }, { "method": "POST", "path": "/api/computers/types", "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'" }, { "method": "PUT", "path": "/api/computers/types/", "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'" }, { "method": "DELETE", "path": "/api/computers/types/", "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'" }, { "method": "GET", "path": "/api/computers/protocols", "auth": "jwt-optional", "params": "active (default true; 'false' includes disabled)", "purpose": "List remote-access protocols (VNC/WinRM/RDP catalog), unpaginated", "example": "curl 'http://localhost:5001/api/computers/protocols?active=false'" }, { "method": "POST", "path": "/api/computers/protocols", "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'" }, { "method": "PUT|PATCH", "path": "/api/computers/protocols/", "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'" }, { "method": "DELETE", "path": "/api/computers/protocols/", "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'" }, { "method": "GET", "path": "/api/computers/display-kiosks", "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'" }, { "method": "GET", "path": "/api/computers", "auth": "jwt-optional", "params": "page, per_page, active, search (assetnumber/name/serialnumber/hostname ilike), assetnumber (exact, for idempotent import), typeid|type_id, osid|os_id, locationid|location_id, businessunitid|businessunit_id, shopfloor (true/false), sort (hostname|assetnumber|name|lastreporteddate), dir (asc|desc)", "purpose": "List computers (asset + computer + resolved access links), filtered and paginated", "example": "curl 'http://localhost:5001/api/computers?shopfloor=true&sort=lastreporteddate&dir=desc&per_page=25'" }, { "method": "GET", "path": "/api/computers/", "auth": "jwt-optional", "params": "computer_id in path", "purpose": "Get one computer with full detail: asset fields, computer extension, communications, resolved access links", "example": "curl 'http://localhost:5001/api/computers/42'" }, { "method": "GET", "path": "/api/computers/by-asset/", "auth": "jwt-optional", "params": "asset_id in path", "purpose": "Get computer record by its core asset ID", "example": "curl 'http://localhost:5001/api/computers/by-asset/1234'" }, { "method": "GET", "path": "/api/computers/by-hostname/", "auth": "jwt-optional", "params": "hostname in path (exact match)", "purpose": "Get computer record by hostname", "example": "curl 'http://localhost:5001/api/computers/by-hostname/tsgwp00525'" }, { "method": "POST", "path": "/api/computers", "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'" }, { "method": "PUT", "path": "/api/computers/", "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'" }, { "method": "DELETE", "path": "/api/computers/", "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'" }, { "method": "GET", "path": "/api/computers//apps", "auth": "jwt-optional", "params": "computer_id in path; returns active installs only", "purpose": "List installed applications on a computer", "example": "curl 'http://localhost:5001/api/computers/42/apps'" }, { "method": "POST", "path": "/api/computers//apps", "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'" }, { "method": "DELETE", "path": "/api/computers//apps/", "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'" }, { "method": "POST", "path": "/api/computers//report", "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\":\"cproudlock\",\"lastboottime\":\"2026-07-30T06:00:00\"}' 'http://localhost:5001/api/computers/42/report'" }, { "method": "GET", "path": "/api/computers/dashboard/summary", "auth": "jwt-optional", "params": "none", "purpose": "Dashboard counts: total active computers, breakdown by type and OS, shopfloor vs non-shopfloor", "example": "curl 'http://localhost:5001/api/computers/dashboard/summary'" } ], "surface": "plugin-computers" }, { "surface": "plugin-measuringtools", "endpoints": [ { "method": "GET", "path": "/api/measuringtools/types", "purpose": "List measuring-tool types (active-only by default), paginated, name-sorted.", "auth": "jwt-optional", "params": "query: active (default true; 'false' includes inactive), search (name ilike), page, perpage", "example": "curl 'http://localhost:5001/api/measuringtools/types?search=caliper&page=1&perpage=25'" }, { "method": "GET", "path": "/api/measuringtools/types/", "purpose": "Get one measuring-tool type by id (404 if missing).", "auth": "jwt-optional", "params": "path: type_id (int)", "example": "curl 'http://localhost:5001/api/measuringtools/types/3'" }, { "method": "POST", "path": "/api/measuringtools/types", "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\"}'" }, { "method": "PUT", "path": "/api/measuringtools/types/", "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}'" }, { "method": "DELETE", "path": "/api/measuringtools/types/", "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'" }, { "method": "GET", "path": "/api/measuringtools", "purpose": "List measuring tools (Asset core merged with extension), filtered + paginated; derived calibrationstatus filter applied post-pagination.", "auth": "jwt-optional", "params": "query: active (default true), assetnumber (exact match, for idempotent import), search (assetnumber/name/serialnumber ilike), typeid, locationid, statusid, calibrationstatus (overdue|duesoon|current|unknown), page, perpage", "example": "curl 'http://localhost:5001/api/measuringtools?typeid=2&calibrationstatus=overdue&page=1&perpage=50'" }, { "method": "GET", "path": "/api/measuringtools/", "purpose": "Get one measuring tool by measuringtoolid, asset core dict with extension nested under 'measuringtool'.", "auth": "jwt-optional", "params": "path: tool_id (int)", "example": "curl 'http://localhost:5001/api/measuringtools/17'" }, { "method": "GET", "path": "/api/measuringtools/by-asset/", "purpose": "Get a measuring tool by its core assetid (404 if the asset has no extension row).", "auth": "jwt-optional", "params": "path: asset_id (int)", "example": "curl 'http://localhost:5001/api/measuringtools/by-asset/1042'" }, { "method": "POST", "path": "/api/measuringtools", "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\"}'" }, { "method": "PUT", "path": "/api/measuringtools/", "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\"}'" }, { "method": "DELETE", "path": "/api/measuringtools/", "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'" }, { "method": "GET", "path": "/api/measuringtools/map-overlay", "purpose": "ADR-010 map overlay: per-asset calibration badges [{assetid, color, label}] for active tools that are overdue or due soon only; no coordinates returned.", "auth": "jwt-optional", "params": "none", "example": "curl 'http://localhost:5001/api/measuringtools/map-overlay'" }, { "method": "GET", "path": "/api/measuringtools/report/calibration", "purpose": "Calibration report for the Reports hub: counts and full tool lists bucketed by derived status (overdue/duesoon/current/unknown) plus statuscolors map.", "auth": "jwt-optional", "params": "none", "example": "curl 'http://localhost:5001/api/measuringtools/report/calibration'" } ] }, { "surface": "plugin-machines", "endpoints": [ { "method": "GET", "path": "/api/machines/types", "purpose": "List machine types (active by default) with pagination and name search", "auth": "jwt-optional", "params": "query: page, per_page, active (pass 'false' to include inactive), search (ilike on machinetype)", "example": "curl 'http://localhost:5001/api/machines/types?search=cnc&page=1&per_page=25'" }, { "method": "GET", "path": "/api/machines/types/", "purpose": "Get a single machine type by ID", "auth": "jwt-optional", "params": "path: type_id (int)", "example": "curl 'http://localhost:5001/api/machines/types/3'" }, { "method": "POST", "path": "/api/machines/types", "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\"}'" }, { "method": "PUT", "path": "/api/machines/types/", "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}'" }, { "method": "DELETE", "path": "/api/machines/types/", "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'" }, { "method": "GET", "path": "/api/machines", "purpose": "List machines (Asset+Machine join) with filters, sorting, pagination; collapses Dualpath dual-bay pairs to one row (annotated with dualpathpartner) when site setting enabled", "auth": "jwt-optional", "params": "query: page, per_page, active ('false' includes inactive), assetnumber (exact-match for idempotent import), search (assetnumber/name/serialnumber ilike), typeid|type_id, vendorid|vendor_id, locationid|location_id, businessunitid|businessunit_id, sort (assetnumber|name), dir (asc|desc)", "example": "curl 'http://localhost:5001/api/machines?search=2007&typeid=2&sort=name&dir=desc&page=1&per_page=50'" }, { "method": "GET", "path": "/api/machines/", "purpose": "Get one machine with full asset details, nested machine dict, and dualpathpartner info", "auth": "jwt-optional", "params": "path: machine_id (int)", "example": "curl 'http://localhost:5001/api/machines/42'" }, { "method": "GET", "path": "/api/machines/by-asset/", "purpose": "Get machine data looked up by core asset ID instead of machine ID", "auth": "jwt-optional", "params": "path: asset_id (int)", "example": "curl 'http://localhost:5001/api/machines/by-asset/1001'" }, { "method": "POST", "path": "/api/machines", "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}'" }, { "method": "PUT", "path": "/api/machines/", "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\"}'" }, { "method": "DELETE", "path": "/api/machines/", "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'" }, { "method": "GET", "path": "/api/machines/dashboard/summary", "purpose": "Dashboard summary: total active machine count plus counts grouped by machine type and by asset status (total and by-type exclude Dualpath secondary bays when collapse setting enabled; by-status does not)", "auth": "jwt-optional", "params": "none", "example": "curl 'http://localhost:5001/api/machines/dashboard/summary'" } ] }, { "surface": "plugin-geenforce", "endpoints": [ { "method": "GET", "path": "/api/geenforce/manifest", "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'" }, { "method": "GET", "path": "/api/geenforce/payload/", "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'" }, { "method": "POST", "path": "/api/geenforce/report", "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\":\"tsgwp00525\",\"scopename\":\"cmm\",\"appliedversion\":4,\"results\":[{\"entryname\":\"7zip\",\"action\":\"installed\"}]}' http://localhost:5001/api/geenforce/report" }, { "method": "GET", "path": "/api/geenforce/scopes", "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" }, { "method": "POST", "path": "/api/geenforce/scopes", "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" }, { "method": "GET", "path": "/api/geenforce/scopes/", "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" }, { "method": "PUT", "path": "/api/geenforce/scopes/", "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" }, { "method": "DELETE", "path": "/api/geenforce/scopes/", "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" }, { "method": "GET", "path": "/api/geenforce/scopes//preview", "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" }, { "method": "GET", "path": "/api/geenforce/applications", "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" }, { "method": "POST", "path": "/api/geenforce/scopes//entries", "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" }, { "method": "PUT", "path": "/api/geenforce/entries/", "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" }, { "method": "DELETE", "path": "/api/geenforce/entries/", "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" }, { "method": "PUT", "path": "/api/geenforce/scopes//entries/reorder", "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" }, { "method": "GET", "path": "/api/geenforce/scopes//simulate", "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=tsgwp00525&cmmversion=2023.2'" }, { "method": "GET", "path": "/api/geenforce/scopes//compliance", "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" }, { "method": "POST", "path": "/api/geenforce/entries//payload", "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" }, { "method": "GET", "path": "/api/geenforce/entries//payload", "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" }, { "method": "POST", "path": "/api/geenforce/scopes//publish", "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" }, { "method": "GET", "path": "/api/geenforce/scopes//versions", "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" }, { "method": "GET", "path": "/api/geenforce/scopes//versions/", "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" }, { "method": "POST", "path": "/api/geenforce/scopes//rollback", "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" }, { "method": "POST", "path": "/api/geenforce/scopes//export-share", "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" }, { "method": "GET", "path": "/api/geenforce/config", "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" }, { "method": "PUT", "path": "/api/geenforce/config", "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" }, { "method": "GET", "path": "/api/geenforce/reports", "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'" }, { "method": "GET", "path": "/api/geenforce/reports/", "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" } ] }, { "surface": "plugin-notifications", "endpoints": [ { "method": "GET", "path": "/api/notifications/types", "purpose": "List notification types, paginated, active-only by default", "auth": "none", "params": "page, per_page; active=false to include inactive types", "example": "curl 'http://localhost:5001/api/notifications/types?active=false&page=1&per_page=50'" }, { "method": "POST", "path": "/api/notifications/types", "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}'" }, { "method": "PUT, PATCH", "path": "/api/notifications/types/", "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}'" }, { "method": "GET", "path": "/api/notifications", "purpose": "List notifications with filters, newest-first, paginated", "auth": "none", "params": "page, per_page; active=false to include inactive; typeid/type_id; ticketnumber (exact match, for idempotent import); current=true (within start/end window now); search (ILIKE on text)", "example": "curl 'http://localhost:5001/api/notifications?current=true&typeid=2&search=outage&page=1'" }, { "method": "GET", "path": "/api/notifications/", "purpose": "Get a single notification by ID", "auth": "none", "params": "path: notification_id", "example": "curl http://localhost:5001/api/notifications/42" }, { "method": "POST", "path": "/api/notifications", "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\"}'" }, { "method": "PUT", "path": "/api/notifications/", "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}'" }, { "method": "DELETE", "path": "/api/notifications/", "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'" }, { "method": "GET", "path": "/api/notifications/active", "purpose": "Currently active notifications for display, incl. ones starting within a 10-day lookahead", "auth": "none", "params": "none", "example": "curl http://localhost:5001/api/notifications/active" }, { "method": "GET", "path": "/api/notifications/calendar", "purpose": "Active notifications as FullCalendar event objects for a date range", "auth": "none", "params": "start (ISO), end (ISO); invalid dates silently ignored", "example": "curl 'http://localhost:5001/api/notifications/calendar?start=2026-07-01T00:00:00Z&end=2026-07-31T23:59:59Z'" }, { "method": "GET", "path": "/api/notifications/dashboard/summary", "purpose": "Dashboard counts: total currently-active notifications plus active counts grouped by type/color", "auth": "none", "params": "none", "example": "curl http://localhost:5001/api/notifications/dashboard/summary" }, { "method": "GET", "path": "/api/notifications/employee/", "purpose": "All active recognition-type notifications mentioning an employee SSO (exact or within comma-separated employeesso list)", "auth": "none", "params": "path: sso (digits only, 400 otherwise)", "example": "curl http://localhost:5001/api/notifications/employee/212345678" }, { "method": "GET", "path": "/api/notifications/shopfloor", "purpose": "Shopfloor TV feed: current cards (active now, or ended <30 min ago flagged resolved) + upcoming (starts within 5 days); splits multi-employee cards per type config, resolves employee names/photos via employees plugin, returns configversion hash so kiosks reload on layout changes", "auth": "none", "params": "businessunit (numeric BU id: returns that BU's plus null-BU notifications; omitted: null-BU only)", "example": "curl 'http://localhost:5001/api/notifications/shopfloor?businessunit=3'" } ] }, { "surface": "plugin-slides", "endpoints": [ { "method": "GET", "path": "/api/slides/feed", "purpose": "Public flat playlist for a surface (lobby/shopfloor); raw jsonify, not the success_response envelope, so the screensaver parser works unchanged", "auth": "none", "params": "query: surface=lobby|shopfloor (default lobby; invalid values fall back to lobby). Returns {success, surface, basepath, interval, slides:[{filename, seconds}]}; only slides whose file exists on disk are listed", "example": "curl 'http://localhost:5001/api/slides/feed?surface=shopfloor'" }, { "method": "GET", "path": "/api/slides/img//", "purpose": "Public serving of a single slide image from instance/slides//, with basename path-traversal guard; 404 on unknown surface, traversal attempt, or missing file", "auth": "none", "params": "path: surface (lobby|shopfloor), filename (must equal its basename)", "example": "curl 'http://localhost:5001/api/slides/img/lobby/Slide1.png' -o Slide1.png" }, { "method": "GET", "path": "/api/slides/", "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'" }, { "method": "POST", "path": "/api/slides//upload", "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'" }, { "method": "POST", "path": "/api/slides//order", "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'" }, { "method": "POST", "path": "/api/slides//delete", "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'" }, { "method": "PATCH", "path": "/api/slides//", "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'" } ] }, { "surface": "plugin-printedparts", "endpoints": [ { "method": "GET", "path": "/api/printedparts/items", "purpose": "List printed items, paginated, with search and low-stock filter", "auth": "jwt + permission:printedparts.view", "params": "query: page, per_page, search (matches itemcode/gagelabtag/itemname/itemdescription/binlocation), active (default true; 'false' includes retired), lowstock=true", "example": "curl -H \"Authorization: Bearer $TOK\" 'http://localhost:5001/api/printedparts/items?search=bracket&lowstock=true&page=1&per_page=25'" }, { "method": "GET", "path": "/api/printedparts/items/", "purpose": "Get one printed item plus its 25 most recent ledger transactions", "auth": "jwt + permission:printedparts.view", "params": "path: item_id", "example": "curl -H \"Authorization: Bearer $TOK\" http://localhost:5001/api/printedparts/items/42" }, { "method": "POST", "path": "/api/printedparts/items", "purpose": "Create a printed item; itemcode auto-minted from printedparts_code_prefix setting + row id; optional gagelabtag unique-checked (409 on clash)", "auth": "jwt + permission:printedparts.create", "params": "json body: itemname (required), gagelabtag, itemdescription, lowstockthreshold (default from printedparts_default_threshold setting, fallback 5), binlocation, printnotes; quantityonhand starts at 0", "example": "curl -X POST -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"itemname\":\"Fixture clip\",\"gagelabtag\":\"WJRP0042\",\"binlocation\":\"B3\",\"lowstockthreshold\":10}' http://localhost:5001/api/printedparts/items" }, { "method": "PUT", "path": "/api/printedparts/items/", "purpose": "Update catalog fields (itemname, itemdescription, lowstockthreshold, binlocation, printnotes, gagelabtag); rejects quantityonhand (ledger-managed) and duplicate gagelabtag (409)", "auth": "jwt + permission:printedparts.edit", "params": "path: item_id; json body: any of the editable fields; gagelabtag uppercased, empty string clears it", "example": "curl -X PUT -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"binlocation\":\"C1\",\"lowstockthreshold\":8}' http://localhost:5001/api/printedparts/items/42" }, { "method": "DELETE", "path": "/api/printedparts/items/", "purpose": "Soft-retire an item (isactive=false); ledger history preserved", "auth": "jwt + permission:printedparts.delete", "params": "path: item_id", "example": "curl -X DELETE -H \"Authorization: Bearer $TOK\" http://localhost:5001/api/printedparts/items/42" }, { "method": "POST", "path": "/api/printedparts/items//restore", "purpose": "Un-retire a soft-deleted item (isactive=true); code, photo, history intact", "auth": "jwt + permission:printedparts.delete", "params": "path: item_id", "example": "curl -X POST -H \"Authorization: Bearer $TOK\" http://localhost:5001/api/printedparts/items/42/restore" }, { "method": "POST", "path": "/api/printedparts/items//image", "purpose": "Upload or replace the item's photo (png/jpg/jpeg/gif/webp); old image files for the item are deleted first, imageurl updated", "auth": "jwt + permission:printedparts.edit", "params": "path: item_id; multipart/form-data: file=", "example": "curl -X POST -H \"Authorization: Bearer $TOK\" -F 'file=@clip.jpg' http://localhost:5001/api/printedparts/items/42/image" }, { "method": "GET", "path": "/api/printedparts/image/", "purpose": "Serve an uploaded item image from instance/printedpartsimages (public: fetched by tags on kiosk and lists)", "auth": "none", "params": "path: filename (e.g. printeditem-42.jpg)", "example": "curl http://localhost:5001/api/printedparts/image/printeditem-42.jpg -o clip.jpg" }, { "method": "DELETE", "path": "/api/printedparts/items//image", "purpose": "Clear the item's imageurl; deletes the file on disk only if the URL is plugin-owned (starts with /api/printedparts/image/)", "auth": "jwt + permission:printedparts.delete", "params": "path: item_id", "example": "curl -X DELETE -H \"Authorization: Bearer $TOK\" http://localhost:5001/api/printedparts/items/42/image" }, { "method": "POST", "path": "/api/printedparts/items//restock", "purpose": "Add freshly printed stock via a 'restock' ledger write (single-commit ledger + cached quantity); badge resolved server-side to sso/name; 404 if item inactive", "auth": "jwt + permission:printedparts.restock", "params": "path: item_id; json body: quantity (positive int, required), badge (required, 422 BadgeError if unresolvable)", "example": "curl -X POST -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"quantity\":20,\"badge\":\"123456789\"}' http://localhost:5001/api/printedparts/items/42/restock" }, { "method": "POST", "path": "/api/printedparts/items//adjust", "purpose": "Correct the count (damage, recount) via an 'adjust' ledger write; reason mandatory; rejects driving stock below zero; fires low-stock alert on downward threshold crossing", "auth": "jwt + permission:printedparts.restock", "params": "path: item_id; json body: quantitychange (non-zero int, required), reason (required), badge (required)", "example": "curl -X POST -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"quantitychange\":-3,\"reason\":\"damaged in bin\",\"badge\":\"123456789\"}' http://localhost:5001/api/printedparts/items/42/adjust" }, { "method": "GET", "path": "/api/printedparts/kiosk/item/", "purpose": "Item summary for a scanned bin barcode; resolves full code (itemcode or gagelabtag), 'TAG|rev' QR payloads (rev stripped), or bare keypad digits when the numeric tail uniquely matches one active item", "auth": "none (deliberately open per decision record; kiosk cannot carry JWT)", "params": "path: itemcode (e.g. WJRP0042, 3DP0042, 42, or WJRP0042|3)", "example": "curl http://localhost:5001/api/printedparts/kiosk/item/WJRP0042" }, { "method": "POST", "path": "/api/printedparts/kiosk/take", "purpose": "Kiosk checkout: decrement-only 'take' ledger write, badge-attributed, bounded by quantityonhand; records optional print-file revision (explicit field or 'TAG|rev' tail); triggers low-stock alert on threshold crossing; the product's only open write", "auth": "none (deliberately open per decision record)", "params": "json body: itemcode (required, same resolution as kiosk/item), badge (required, resolved server-side, 422 on BadgeError), quantity (positive int <= on hand), revision (optional int)", "example": "curl -X POST -H 'Content-Type: application/json' -d '{\"itemcode\":\"WJRP0042|3\",\"badge\":\"123456789\",\"quantity\":2}' http://localhost:5001/api/printedparts/kiosk/take" }, { "method": "GET", "path": "/api/printedparts/reports/stock", "purpose": "Stock-level report for active items with low-stock flags and ledgerdelta (cache-vs-ledger reconcile; nonzero means a write bypassed the single-commit rule)", "auth": "jwt-optional (like all product reports)", "params": "query: format=csv for CSV download (printedparts-stock.csv), else JSON {columns, rows}", "example": "curl 'http://localhost:5001/api/printedparts/reports/stock?format=csv' -o stock.csv" }, { "method": "GET", "path": "/api/printedparts/reports/consumption", "purpose": "Take-transactions aggregated per item (takes count + quantitytaken), sorted by quantity taken descending", "auth": "jwt-optional", "params": "query: days (default 30; 0 or negative = all time), format=csv (printedparts-consumption.csv)", "example": "curl 'http://localhost:5001/api/printedparts/reports/consumption?days=90'" }, { "method": "GET", "path": "/api/printedparts/reports/by-person", "purpose": "Take-transactions grouped by employee SSO (takes count + quantitytaken), sorted by quantity taken descending", "auth": "jwt-optional", "params": "query: days (default 30; 0 or negative = all time), format=csv (printedparts-by-person.csv)", "example": "curl 'http://localhost:5001/api/printedparts/reports/by-person?days=30&format=csv' -o by-person.csv" }, { "method": "GET", "path": "/api/printedparts/items//files", "purpose": "List the item's print-file revision history, newest revision first", "auth": "jwt + permission:printedparts.view", "params": "path: item_id", "example": "curl -H \"Authorization: Bearer $TOK\" http://localhost:5001/api/printedparts/items/42/files" }, { "method": "POST", "path": "/api/printedparts/items//files", "purpose": "Upload the next print-file revision (append-only, auto-numbered max+1); allowed ext: stl/3mf/gcode/gco/bgcode/step/stp/obj/amf; 100 MB cap; uploader recorded from JWT identity", "auth": "jwt + permission:printedparts.edit", "params": "path: item_id; multipart/form-data: file= (required), note= (optional)", "example": "curl -X POST -H \"Authorization: Bearer $TOK\" -F 'file=@clip-v2.stl' -F 'note=thicker wall' http://localhost:5001/api/printedparts/items/42/files" }, { "method": "GET", "path": "/api/printedparts/files//download", "purpose": "Download a print-file revision as an attachment under its original filename (jwt-optional so plain anchor downloads work)", "auth": "jwt-optional", "params": "path: file_id", "example": "curl -OJ http://localhost:5001/api/printedparts/files/7/download" }, { "method": "DELETE", "path": "/api/printedparts/files/", "purpose": "Delete a bad print-file revision (wrong file uploaded): removes the stored file and the DB record", "auth": "jwt + permission:printedparts.delete", "params": "path: file_id", "example": "curl -X DELETE -H \"Authorization: Bearer $TOK\" http://localhost:5001/api/printedparts/files/7" } ] }, { "endpoints": [ { "method": "GET", "path": "/api/network/types", "purpose": "List network device types, paginated", "auth": "jwt-optional", "params": "page, per_page, active (default true; 'false' includes inactive), search (ilike on networkdevicetype)", "example": "curl 'http://localhost:5001/api/network/types?search=switch&per_page=50'" }, { "method": "GET", "path": "/api/network/types/", "purpose": "Get one network device type by ID", "auth": "jwt-optional", "params": "path: type_id", "example": "curl http://localhost:5001/api/network/types/3" }, { "method": "POST", "path": "/api/network/types", "purpose": "Create a network device type (reactivates a soft-deleted duplicate instead of 409ing)", "auth": "jwt + permission:network.create", "params": "body: networkdevicetype (required), description, icon, color", "example": "curl -X POST http://localhost:5001/api/network/types -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"networkdevicetype\":\"Switch\",\"icon\":\"mdi-switch\",\"color\":\"#2196f3\"}'" }, { "method": "PUT", "path": "/api/network/types/", "purpose": "Update a network device type; 409 on name collision", "auth": "jwt + permission:network.edit", "params": "body: networkdevicetype, description, icon, color, isactive", "example": "curl -X PUT http://localhost:5001/api/network/types/3 -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"description\":\"Access switches\"}'" }, { "method": "DELETE", "path": "/api/network/types/", "purpose": "Hard-delete a device type; 409 if any device still references it", "auth": "jwt + permission:network.delete", "params": "path: type_id", "example": "curl -X DELETE http://localhost:5001/api/network/types/3 -H \"Authorization: Bearer $TOKEN\"" }, { "method": "GET", "path": "/api/network", "purpose": "List network devices (Asset joined with NetworkDevice extension + primary IP), filtered/sorted/paginated", "auth": "jwt-optional", "params": "page, per_page, active, assetnumber (exact match for idempotent import), search (assetnumber/name/serialnumber/hostname), typeid|type_id, vendorid|vendor_id, locationid|location_id, businessunitid|businessunit_id, poe (true/false), managed (true/false), sort (hostname|assetnumber|name), dir (asc|desc)", "example": "curl 'http://localhost:5001/api/network?typeid=2&poe=true&sort=hostname&dir=asc'" }, { "method": "GET", "path": "/api/network/", "purpose": "Get one network device (asset dict + networkdevice sub-object + primary ipaddress)", "auth": "jwt-optional", "params": "path: device_id (networkdeviceid)", "example": "curl http://localhost:5001/api/network/17" }, { "method": "GET", "path": "/api/network/by-asset/", "purpose": "Look up a network device by its core assetid", "auth": "jwt-optional", "params": "path: asset_id", "example": "curl http://localhost:5001/api/network/by-asset/1042" }, { "method": "GET", "path": "/api/network/by-hostname/", "purpose": "Look up a network device by exact hostname", "auth": "jwt-optional", "params": "path: hostname", "example": "curl http://localhost:5001/api/network/by-hostname/wjf-sw-idf3-01" }, { "method": "POST", "path": "/api/network", "purpose": "Create a network device (creates core Asset + NetworkDevice extension; upserts primary-IP Communication; audit-logged; honors X-Import-Mode legacy timestamps)", "auth": "jwt + permission:network.create", "params": "body: assetnumber (required); name, serialnumber, gaugelabreference, maintenancereference, statusid (default 1), locationid, businessunitid, mapx, mapy, notes, networkdevicetypeid, vendorid, hostname (unique), firmwareversion, portcount, ispoe, ismanaged, rackunit, ipaddress", "example": "curl -X POST http://localhost:5001/api/network -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"assetnumber\":\"NET-0042\",\"hostname\":\"wjf-sw-idf3-01\",\"networkdevicetypeid\":1,\"portcount\":48,\"ispoe\":true,\"ipaddress\":\"10.1.3.10\"}'" }, { "method": "PUT", "path": "/api/network/", "purpose": "Update asset + network-device fields; 409 on assetnumber/hostname conflicts; change-diff audit log; upserts primary IP when ipaddress present", "auth": "jwt + permission:network.edit", "params": "body: any of assetnumber, name, serialnumber, gaugelabreference, maintenancereference, statusid, locationid, businessunitid, mapx, mapy, notes, isactive, networkdevicetypeid, vendorid, hostname, firmwareversion, portcount, ispoe, ismanaged, rackunit, ipaddress", "example": "curl -X PUT http://localhost:5001/api/network/17 -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"firmwareversion\":\"16.12.4\",\"ipaddress\":\"10.1.3.11\"}'" }, { "method": "DELETE", "path": "/api/network/", "purpose": "Soft-delete a network device (sets asset.isactive=false; audit-logged)", "auth": "jwt + permission:network.delete", "params": "path: device_id", "example": "curl -X DELETE http://localhost:5001/api/network/17 -H \"Authorization: Bearer $TOKEN\"" }, { "method": "GET", "path": "/api/network/dashboard/summary", "purpose": "Dashboard counts: total active devices, by type, by vendor, PoE vs non-PoE", "auth": "jwt-optional", "params": "none", "example": "curl http://localhost:5001/api/network/dashboard/summary" }, { "method": "GET", "path": "/api/network/vlans", "purpose": "List VLANs, paginated, ordered by vlannumber", "auth": "jwt-optional", "params": "page, per_page, active (default true), search (name/description/vlannumber), type (exact vlantype)", "example": "curl 'http://localhost:5001/api/network/vlans?search=voice'" }, { "method": "GET", "path": "/api/network/vlans/", "purpose": "Get one VLAN including its active subnets", "auth": "jwt-optional", "params": "path: vlan_id", "example": "curl http://localhost:5001/api/network/vlans/5" }, { "method": "POST", "path": "/api/network/vlans", "purpose": "Create a VLAN; 409 on duplicate vlannumber; audit-logged", "auth": "jwt + permission:network.create", "params": "body: vlannumber (required), name (required), description, vlantype", "example": "curl -X POST http://localhost:5001/api/network/vlans -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"vlannumber\":120,\"name\":\"Shopfloor\",\"vlantype\":\"production\"}'" }, { "method": "PUT", "path": "/api/network/vlans/", "purpose": "Update a VLAN; 409 on vlannumber conflict; change-diff audit log", "auth": "jwt + permission:network.edit", "params": "body: vlannumber, name, description, vlantype, isactive", "example": "curl -X PUT http://localhost:5001/api/network/vlans/5 -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"description\":\"CNC cell VLAN\"}'" }, { "method": "DELETE", "path": "/api/network/vlans/", "purpose": "Soft-delete a VLAN; 400 if it still has active subnets; audit-logged", "auth": "jwt + permission:network.delete", "params": "path: vlan_id", "example": "curl -X DELETE http://localhost:5001/api/network/vlans/5 -H \"Authorization: Bearer $TOKEN\"" }, { "method": "GET", "path": "/api/network/subnets", "purpose": "List subnets, paginated, ordered by cidr", "auth": "jwt-optional", "params": "page, per_page, active (default true), search (cidr/name/description), vlanid, locationid, type (exact subnettype)", "example": "curl 'http://localhost:5001/api/network/subnets?vlanid=5'" }, { "method": "GET", "path": "/api/network/subnets/", "purpose": "Get one subnet plus 'devices': every asset of any type (PC/printer/network) whose primary-IP Communication falls inside the CIDR, with cross-plugin detail URLs", "auth": "jwt-optional", "params": "path: subnet_id", "example": "curl http://localhost:5001/api/network/subnets/2" }, { "method": "POST", "path": "/api/network/subnets", "purpose": "Create a subnet; validates CIDR notation and vlanid existence; 409 on duplicate cidr; audit-logged", "auth": "jwt + permission:network.create", "params": "body: cidr (required, must contain '/'), name (required), description, gatewayip, subnetmask, networkaddress, broadcastaddress, vlanid, subnettype, locationid, dhcpenabled (default true), dhcprangestart, dhcprangeend, dns1, dns2", "example": "curl -X POST http://localhost:5001/api/network/subnets -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"cidr\":\"10.1.3.0/24\",\"name\":\"IDF3 access\",\"vlanid\":5,\"gatewayip\":\"10.1.3.1\"}'" }, { "method": "PUT", "path": "/api/network/subnets/", "purpose": "Update a subnet; 409 on cidr conflict; change-diff audit log", "auth": "jwt + permission:network.edit", "params": "body: any of cidr, name, description, gatewayip, subnetmask, networkaddress, broadcastaddress, vlanid, subnettype, locationid, dhcpenabled, dhcprangestart, dhcprangeend, dns1, dns2, isactive", "example": "curl -X PUT http://localhost:5001/api/network/subnets/2 -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"dhcpenabled\":false}'" }, { "method": "DELETE", "path": "/api/network/subnets/", "purpose": "Soft-delete a subnet (isactive=false); audit-logged", "auth": "jwt + permission:network.delete", "params": "path: subnet_id", "example": "curl -X DELETE http://localhost:5001/api/network/subnets/2 -H \"Authorization: Bearer $TOKEN\"" } ], "surface": "plugin-network" }, { "surface": "plugin-usb", "endpoints": [ { "method": "GET", "path": "/api/usb", "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'" }, { "method": "GET", "path": "/api/usb/", "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'" }, { "method": "POST", "path": "/api/usb", "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\"}'" }, { "method": "PUT", "path": "/api/usb/", "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\"}'" }, { "method": "POST", "path": "/api/usb//retire", "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'" }, { "method": "POST", "path": "/api/usb//checkout", "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\"}'" }, { "method": "POST", "path": "/api/usb//checkin", "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}'" }, { "method": "GET", "path": "/api/usb//history", "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'" }, { "method": "GET", "path": "/api/usb/checkouts", "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'" }, { "method": "GET", "path": "/api/usb/checkouts/active", "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'" } ] }, { "surface": "plugin-printers", "endpoints": [ { "method": "GET", "path": "/api/printers/types", "auth": "jwt-optional", "params": "page, per_page, active (default true; 'false' includes inactive), search", "purpose": "List printer types, paginated, with active filter and name search.", "example": "curl -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/printers/types?search=laser&active=false'" }, { "method": "GET", "path": "/api/printers/types/", "auth": "jwt-optional", "params": "path: type_id (int)", "purpose": "Get a single printer type by ID.", "example": "curl http://localhost:5001/api/printers/types/3" }, { "method": "POST", "path": "/api/printers/types", "auth": "permission:printers.create", "params": "body: printertype (required), description, icon, color; reactivates a matching inactive type instead of 409", "purpose": "Create a new printer type (or reactivate an inactive duplicate).", "example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"printertype\":\"Label\",\"color\":\"#00f\"}' http://localhost:5001/api/printers/types" }, { "method": "PUT", "path": "/api/printers/types/", "auth": "permission:printers.edit", "params": "body: printertype, description, icon, color, isactive (any subset); 409 on name clash", "purpose": "Update a printer type.", "example": "curl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"isactive\":false}' http://localhost:5001/api/printers/types/3" }, { "method": "DELETE", "path": "/api/printers/types/", "auth": "permission:printers.delete", "params": "path: type_id; 409 if any printer still references the type", "purpose": "Hard-delete a printer type when unused.", "example": "curl -X DELETE -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/types/3" }, { "method": "GET", "path": "/api/printers/drivers", "auth": "jwt-optional", "params": "active (default true; 'false' includes inactive). Unpaginated.", "purpose": "List printer driver packages (named SMB/HTTP links).", "example": "curl 'http://localhost:5001/api/printers/drivers?active=false'" }, { "method": "POST", "path": "/api/printers/drivers", "auth": "permission:printers.create", "params": "body: name (required), location (required), description, modelnumberid, isactive", "purpose": "Create a driver entry.", "example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"name\":\"HP UPD\",\"location\":\"\\\\\\\\fileserver\\\\drivers\\\\hpupd\"}' http://localhost:5001/api/printers/drivers" }, { "method": "PUT", "path": "/api/printers/drivers/", "auth": "permission:printers.edit", "params": "body: name, location, description, isactive, modelnumberid (any subset)", "purpose": "Update a driver entry.", "example": "curl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"isactive\":false}' http://localhost:5001/api/printers/drivers/5" }, { "method": "DELETE", "path": "/api/printers/drivers/", "auth": "permission:printers.delete", "params": "path: driver_id", "purpose": "Hard-delete a driver entry.", "example": "curl -X DELETE -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/drivers/5" }, { "method": "GET", "path": "/api/printers", "auth": "jwt-optional", "params": "page, per_page, active, assetnumber (exact-match for import idempotency), search (assetnumber/name/serial/hostname/windowsname), typeid|type_id, vendorid|vendor_id, locationid|location_id, businessunitid|businessunit_id, sort (hostname|assetnumber|name), dir (asc|desc)", "purpose": "List printers (joined Asset+Printer) with filters, search, sorting, pagination; each row includes primary IP.", "example": "curl 'http://localhost:5001/api/printers?search=csf&typeid=2&sort=assetnumber&dir=desc'" }, { "method": "GET", "path": "/api/printers/install-list", "auth": "jwt-optional", "params": "format=text for pipe-delimited installer variant (printerid|windowsname|vendorname|modelnumber|hostname|ipaddress|mapx|mapy); default JSON", "purpose": "Flat unpaginated list of active network printers (with map positions) for the signed printer-installer EXE; replaces classic apiprinters.asp.", "example": "curl 'http://localhost:5001/api/printers/install-list?format=text'" }, { "method": "GET", "path": "/api/printers/install-batch", "auth": "jwt-optional", "params": "printerids (required, comma-separated printer IDs)", "purpose": "Generate and download a Windows .bat that installs selected printers (HP/Xerox via universal PrinterInstaller.exe, per-printer .exe /SILENT, or flags manual).", "example": "curl -OJ 'http://localhost:5001/api/printers/install-batch?printerids=1,2,3'" }, { "method": "GET", "path": "/api/printers/pc-default", "auth": "jwt-optional", "params": "machine (PC asset number), format=text for pipe-delimited variant; returns {} / empty body if no default set", "purpose": "Look up a PC's default printer via the defaultprinter asset relationship (parity with classic apipcdefaultprinter.asp); used by installer to preselect map hotspot.", "example": "curl 'http://localhost:5001/api/printers/pc-default?machine=0421&format=text'" }, { "method": "GET", "path": "/api/printers/", "auth": "jwt-optional", "params": "path: printer_id (int)", "purpose": "Get one printer with full asset details, communications, and active drivers matching its model.", "example": "curl http://localhost:5001/api/printers/17" }, { "method": "GET", "path": "/api/printers/by-asset/", "auth": "jwt-optional", "params": "path: asset_id (int)", "purpose": "Get printer data keyed by core asset ID.", "example": "curl http://localhost:5001/api/printers/by-asset/204" }, { "method": "POST", "path": "/api/printers", "auth": "permission:printers.create", "params": "body: assetnumber (required); name, serialnumber, gaugelabreference, maintenancereference, statusid, locationid, businessunitid, printertypeid, vendorid, modelnumberid, hostname, windowsname, sharename, iscsf, installpath, pin, iscolor, isduplex, isnetwork, mapx, mapy, notes, ipaddress (creates primary IP comm); X-Import-Mode header preserves legacy timestamps", "purpose": "Create a printer (Asset + Printer extension + optional primary IP communication).", "example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"assetnumber\":\"PR-0042\",\"hostname\":\"wjprn042\",\"ipaddress\":\"10.1.2.42\",\"printertypeid\":1}' http://localhost:5001/api/printers" }, { "method": "PUT", "path": "/api/printers/", "auth": "permission:printers.edit", "params": "body: any subset of asset fields (assetnumber, name, serialnumber, gaugelabreference, maintenancereference, statusid, locationid, businessunitid, mapx, mapy, notes, isactive) + printer fields (printertypeid, vendorid, modelnumberid, hostname, windowsname, sharename, iscsf, installpath, pin, iscolor, isduplex, isnetwork) + ipaddress (upserts/clears primary IP comm); 409 on assetnumber clash", "purpose": "Update printer: asset, extension, and primary IP in one call.", "example": "curl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"ipaddress\":\"10.1.2.99\",\"iscolor\":true}' http://localhost:5001/api/printers/17" }, { "method": "DELETE", "path": "/api/printers/", "auth": "permission:printers.delete", "params": "path: printer_id", "purpose": "Soft-delete a printer (sets the underlying asset isactive=false).", "example": "curl -X DELETE -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/17" }, { "method": "GET", "path": "/api/printers//supplies", "auth": "jwt-optional", "params": "path: printer_id; needs an IP communication on the printer", "purpose": "Real-time supply levels from Zabbix for one printer, annotated with status/color/part numbers; fails soft (empty supplies, pingstatus -1) when Zabbix is off/unreachable.", "example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/17/supplies" }, { "method": "GET", "path": "/api/printers/lowsupplies", "auth": "jwt-optional", "params": "none (data cached 5 min)", "purpose": "Fleet-wide report of printers with low/critical supply levels from Zabbix, with summary counts.", "example": "curl http://localhost:5001/api/printers/lowsupplies" }, { "method": "GET", "path": "/api/printers/lookup", "auth": "jwt-optional", "params": "ip or fqdn (one required; value matched against communication ipaddress)", "purpose": "Find a printer by IP/FQDN (parity with classic printerlookup.asp; used by Zabbix to deep-link to a printer record).", "example": "curl 'http://localhost:5001/api/printers/lookup?ip=10.1.2.42'" }, { "method": "POST", "path": "/api/printers/supplies/refresh", "auth": "permission:printers.create", "params": "none", "purpose": "Clear the cached Zabbix supply data so next reads pull fresh values (toner-report Refresh button).", "example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/supplies/refresh" }, { "method": "GET", "path": "/api/printers/dashboard/summary", "auth": "jwt-optional", "params": "none", "purpose": "Dashboard summary: total active printers, counts by type and vendor, low/critical supply counts (Zabbix, if reachable).", "example": "curl http://localhost:5001/api/printers/dashboard/summary" }, { "method": "GET", "path": "/api/printers/supplies/meta", "auth": "jwt-optional", "params": "none", "purpose": "Allowed enum values for supplytype, color, and capacitytier (for the UI forms).", "example": "curl http://localhost:5001/api/printers/supplies/meta" }, { "method": "GET", "path": "/api/printers/models", "auth": "jwt-optional", "params": "page, per_page, search (modelnumber), vendorid|vendor_id, withsupplies=true (only models that already have supplies)", "purpose": "List printer models with supply counts for the supply-management picker (restricted to models attached to printers or already carrying supplies).", "example": "curl 'http://localhost:5001/api/printers/models?withsupplies=true&search=M404'" }, { "method": "GET", "path": "/api/printers/models//supplies", "auth": "jwt-optional", "params": "path: modelnumberid (int)", "purpose": "List all active supplies (toner/drum/waste part numbers) mapped to a model.", "example": "curl http://localhost:5001/api/printers/models/12/supplies" }, { "method": "POST", "path": "/api/printers/models//supplies", "auth": "permission:printers.create", "params": "body: partnumber (required), supplytype (default toner), color (default none), capacitytier (default standard), marketingname, pageyield, notes; 409 if part already mapped to model", "purpose": "Add a supply part-number mapping to a model.", "example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"partnumber\":\"CF259A\",\"supplytype\":\"toner\",\"color\":\"black\"}' http://localhost:5001/api/printers/models/12/supplies" }, { "method": "PUT", "path": "/api/printers/supplies/", "auth": "permission:printers.edit", "params": "body: any subset of supplytype, color, capacitytier, partnumber, marketingname, pageyield, notes; enum-validated, 409 on partnumber clash within model", "purpose": "Update a model supply mapping.", "example": "curl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"capacitytier\":\"high\"}' http://localhost:5001/api/printers/supplies/44" }, { "method": "DELETE", "path": "/api/printers/supplies/", "auth": "permission:printers.delete", "params": "path: modelsupplyid", "purpose": "Hard-delete a model supply mapping.", "example": "curl -X DELETE -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/supplies/44" } ] }, { "surface": "plugin-warranty", "endpoints": [ { "method": "GET", "path": "/api/warranty", "purpose": "List warranties with linked-asset summaries and derived status (batch asset fetch, ordered by enddate with null last).", "auth": "jwt-optional", "params": "query: active (default true; 'false' includes inactive), servicetag (exact match, for idempotent import), vendor (exact match), assetid (int, filter to warranties covering that asset), status (post-filter on derived status: expired|expiring|active|unknown)", "example": "curl -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/warranty?status=expiring&assetid=42'" }, { "method": "GET", "path": "/api/warranty/asset/", "purpose": "Active warranties covering one asset, for the asset-detail panel.", "auth": "jwt-optional", "params": "path: assetid", "example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/warranty/asset/42" }, { "method": "GET", "path": "/api/warranty/", "purpose": "Fetch a single warranty by id with asset summaries; 404 if missing.", "auth": "jwt-optional", "params": "path: warrantyid", "example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/warranty/7" }, { "method": "POST", "path": "/api/warranty", "purpose": "Create a warranty (vendor required) and optionally link it to assets; returns 201.", "auth": "jwt + permission:warranty.create", "params": "JSON body: vendor (required), servicetag, provider (default 'manual', lowercased), servicelevel, startdate (YYYY-MM-DD), enddate (YYYY-MM-DD), notes, assetids (list of asset ids to link)", "example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"vendor\":\"Dell\",\"servicetag\":\"ABC1234\",\"enddate\":\"2027-06-30\",\"assetids\":[42]}' http://localhost:5001/api/warranty" }, { "method": "PUT", "path": "/api/warranty/", "purpose": "Partial update of any warranty field (only keys present in body change), including isactive and replacing asset links via assetids.", "auth": "jwt + permission:warranty.edit", "params": "path: warrantyid; JSON body (all optional): vendor, servicetag, provider, servicelevel, startdate, enddate, notes, isactive (bool), assetids (full replacement list)", "example": "curl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"enddate\":\"2028-01-15\",\"assetids\":[42,43]}' http://localhost:5001/api/warranty/7" }, { "method": "DELETE", "path": "/api/warranty/", "purpose": "Hard-delete a warranty (and its asset links); 404 if missing.", "auth": "jwt + permission:warranty.delete", "params": "path: warrantyid", "example": "curl -X DELETE -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/warranty/7" }, { "method": "POST", "path": "/api/warranty//refresh", "purpose": "Re-query the warranty's provider (dell etc.) by service tag and update servicelevel/startdate/enddate + lastcheckeddate; 400 if provider is manual or not configured.", "auth": "jwt + permission:warranty.edit", "params": "path: warrantyid; no body", "example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/warranty/7/refresh" }, { "method": "POST", "path": "/api/warranty/sync/dell", "purpose": "Bulk Dell sync: look up serials of active assets lacking a dated warranty via Dell bulk_lookup, creating or updating (canonicalizing to provider dell) warranties; returns candidates/tags/matched/created/updated counts.", "auth": "jwt + permission:warranty.edit", "params": "query: all (default false; 'true' re-checks assets that already have a dated warranty); no body", "example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" 'http://localhost:5001/api/warranty/sync/dell?all=true'" }, { "method": "GET", "path": "/api/warranty/report", "purpose": "Report for the Reports hub: active warranties bucketed by derived status (expired/expiring/active/unknown) with per-bucket counts and full lists.", "auth": "jwt-optional", "params": "none", "example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/warranty/report" } ] } ]