Closes the three defects in the imported restore path, at Ben's request. Each was proven against a real stack before and after: two backup sets with different data and different instance files, then a restore of the OLDER one, which is the case that was broken. A restore no longer depends on a tab staying open. The backup sidecar has no Flask, so it loads the dump and leaves a sentinel for the api container to run the migrations. The only thing that ever acted on that sentinel was the poll loop of the modal that queued the restore, so closing the tab - or letting a token expire - during the minutes a load takes left the data at the old revision under new code, every query on a newer column failing, and nothing retrying. A watcher in the api container now claims and runs it. The endpoint no longer runs the upgrade at all; it only makes sure something is pending, which is also how a failed upgrade is retried. That takes Alembic out of the request path, where it could outrun the gunicorn worker timeout and be killed mid-chain, leaving a half-applied revision the next attempt trips over. The claim is a rename, because rename(2) is atomic: several workers and a browser poll can race the same sentinel and exactly one wins. A failed upgrade parks itself as .upgrade-failed rather than re-arming, since putting it back would rerun the same broken chain on every poll forever. Instance files come from the set being restored. The restic call asked for 'latest' regardless of which set was named, so restoring anything but the newest paired that set's DATABASE with the NEWEST snapshot's uploads and plugins.json - a mismatch nothing downstream could detect. Now scoped by the set's own tag, which backup.sh has always written. The tree is replaced rather than restored over, so files created after the snapshot are gone afterwards: a restore has to put the site back to the snapshot, not merge into it. Done as a staged swap rather than 'restic restore --delete --target /', because that aims a delete pass at the container root and the cost of getting its filter wrong is the whole filesystem. Nothing destructive happens before a copy is taken. The dump carries DROP TABLE per table with no transaction around it, and the instance tree is replaced wholesale, so a wrong set or a failure part-way through left nothing to go back to. restore.sh now writes a pre-restore-<stamp>/ set - dump, instance tarball and a README saying what it is and how to load it - and refuses to touch anything if that copy cannot be written. Two more found while proving the above, both of which stopped the overlay working at all: * Every script under scripts/backup/ was committed non-executable, while every other .sh in the repo is 755. The compose entrypoints are /opt/backup/watch.sh and backup.sh from a read-only bind mount, so the backup and watcher containers could not start from a clean checkout on any machine, and nothing could fix it at runtime. * Dockerfile.backup could not build without the corporate CA staged. A plain COPY of the gitignored cert fails outright; the trailing-glob form does too, because BuildKit fails a COPY whose pattern matches nothing - which is exactly the case being handled. Copying the directory and testing for the file inside it is the form that works both ways. Verified by building the image with no cert present. Suite 2600 passed / 9 skipped. The finalizer has unit tests covering the atomic claim, the parked failure and the endpoint no longer running the upgrade. Scope: product
4360 lines
255 KiB
JSON
4360 lines
255 KiB
JSON
[
|
|
{
|
|
"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/<userid>",
|
|
"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/<userid>",
|
|
"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/<userid>",
|
|
"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/<roleid>",
|
|
"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/<roleid>",
|
|
"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/<tokenid>",
|
|
"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/<tokenid>",
|
|
"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/<key>",
|
|
"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/<key>",
|
|
"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_<theme> 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/<path:filename>",
|
|
"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/<path:filename>",
|
|
"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"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/settings/asset-import/script",
|
|
"auth": "admin role",
|
|
"params": "none",
|
|
"purpose": "Download Collect-ShopDB.ps1 with this site CSV path already embedded, so the collector script needs no hand editing before it runs",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/settings/asset-import/script -o Collect-ShopDB.ps1"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/settings/asset-import/templates",
|
|
"auth": "admin role",
|
|
"params": "none",
|
|
"purpose": "List the importable CSV tables in the order they must be uploaded - foreign keys mean the order is not optional",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/settings/asset-import/templates"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/settings/asset-import/templates/<tablename>",
|
|
"auth": "admin role",
|
|
"params": "path: tablename",
|
|
"purpose": "Download a generated CSV template for one importable table, with its real column names as the header row",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/settings/asset-import/templates/assets -o assets.csv"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/settings/asset-import/import",
|
|
"auth": "admin role",
|
|
"params": "multipart: one file per table; form: commit (false = validate only)",
|
|
"purpose": "Validate uploaded CSVs and, only when commit=true, load them. The default is a dry run that reports what WOULD change; a committing run writes an AuditLog row with the reconciled count",
|
|
"example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" -F \"assets=@assets.csv\" -F \"commit=true\" http://localhost:5001/api/settings/asset-import/import"
|
|
},
|
|
{
|
|
"path": "/api/settings/asset-import/winc/preview",
|
|
"method": "POST",
|
|
"auth": "jwt + require_role admin",
|
|
"params": "multipart/form-data: files (one WINC/Maximo CSV export)",
|
|
"purpose": "Dry run a WINC machine-adoption import. Writes nothing; returns the plan, a summary, the needs-create groups an admin must acknowledge, and the planhash that authorises the matching commit",
|
|
"example": "curl -X POST http://localhost:5001/api/settings/asset-import/winc/preview -H \"Authorization: Bearer $TOK\" -F files=@winc.csv"
|
|
},
|
|
{
|
|
"path": "/api/settings/asset-import/winc/commit",
|
|
"method": "POST",
|
|
"auth": "jwt + require_role admin",
|
|
"params": "multipart/form-data: files (the same export); form: planhash (REQUIRED, from the preview), acknowledge (comma-separated needs-create group names)",
|
|
"purpose": "Apply a previewed WINC plan in one transaction, journalled so it can be undone. planhash is required and must match a freshly rebuilt plan: without it the drift check compared a packet against the plan it was built from and could never fail, so an admin could approve one plan and apply another",
|
|
"example": "curl -X POST http://localhost:5001/api/settings/asset-import/winc/commit -H \"Authorization: Bearer $TOK\" -F files=@winc.csv -F planhash=$HASH -F acknowledge=businessunit"
|
|
},
|
|
{
|
|
"path": "/api/settings/asset-import/winc/batches",
|
|
"method": "GET",
|
|
"auth": "jwt + require_role admin",
|
|
"params": "none",
|
|
"purpose": "List applied and undone import batches, newest first, with kind, row count, who applied it and when",
|
|
"example": "curl http://localhost:5001/api/settings/asset-import/winc/batches -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"path": "/api/settings/asset-import/winc/undo",
|
|
"method": "POST",
|
|
"auth": "jwt + require_role admin",
|
|
"params": "body: batchid (required)",
|
|
"purpose": "Reverse an applied batch by replaying its journal backwards. Conservative: a cell is put back only if it still holds what the import wrote, and a created row is deleted only when nothing outside the batch references it",
|
|
"example": "curl -X POST http://localhost:5001/api/settings/asset-import/winc/undo -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"batchid\":\"...\"}'"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"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 (including the gaugelabreference and maintenancereference identifiers), applications, KB, employees, USB devices, printed items, notifications, custom fields, hostnames, IPs/subnets, vendor/model/type; ServiceNOW ticket prefixes and smart redirects; results capped at 50, types filterable via search_<type>_enabled settings. Retired (isactive=0) rows are excluded everywhere, including KB articles whose topic application is retired",
|
|
"auth": "jwt-optional",
|
|
"params": "q (required, 2-200 chars)",
|
|
"example": "curl 'http://localhost:5001/api/search?q=WKSTN0042'"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/search/suggest",
|
|
"purpose": "A better spelling for a query that found nothing, or null. Its own endpoint so any list page can ask after rendering no rows, rather than a suggestion key added to every search route. The vocabulary is harvested from searched columns (asset numbers and names, vendors, models, applications, asset types) and cached, so site-specific words are correctable; digits must match exactly, which keeps CSF16 from being 'corrected' to CSF17. Suggests, never rewrites",
|
|
"auth": "jwt-optional",
|
|
"params": "q (required, 2-200 chars)",
|
|
"example": "curl 'http://localhost:5001/api/search/suggest?q=keyecne'"
|
|
},
|
|
{
|
|
"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/capabilities",
|
|
"purpose": "Booleans describing what THIS caller may reach, so the app chrome can avoid drawing controls that would only answer 401; cansearch reports whether the caller holds assets.view, which a signed-out visitor does when the guest role has been granted it",
|
|
"auth": "none",
|
|
"params": "none",
|
|
"example": "curl http://localhost:5001/api/dashboard/capabilities"
|
|
},
|
|
{
|
|
"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.example.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/<int:default_id>",
|
|
"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/<int:default_id>",
|
|
"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/<pluginname>",
|
|
"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_<NAME> or shared COLLECTOR_API_KEY, or a collector.ingest-scoped managed PAT via Bearer/X-API-Key)",
|
|
"params": "body: JSON payload whose schema identityfield (e.g. hostname) is required; rest is plugin-defined",
|
|
"example": "curl -X POST http://localhost:5001/api/collector/computers -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{\"hostname\":\"WKSTN0042\",\"serialnumber\":\"ABC123\"}'"
|
|
},
|
|
{
|
|
"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\":\"WKSTN0042\",\"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\":\"WKSTN0042\",\"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/<entitytype>/<int:entityid>",
|
|
"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/auditlogs/retention",
|
|
"auth": "jwt + audit.view",
|
|
"params": "none",
|
|
"purpose": "Audit log size (total entries, oldest/newest timestamps) and, when a retention period is configured, how many entries a purge would delete. Drives the Site & Facility retention card so the count is seen before anything is deleted",
|
|
"example": "curl http://localhost:5001/api/auditlogs/retention -H \"Authorization: Bearer $TOKEN\""
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/auditlogs/purge",
|
|
"auth": "jwt + audit.purge",
|
|
"params": "body: days (optional, defaults to the audit_retentiondays setting), dryrun (optional bool)",
|
|
"purpose": "Delete audit entries older than the retention window, in committed batches. Requires audit.purge, not merely audit.view. Batch-capped so the request cannot outlive a proxy timeout; an incomplete run returns complete=false with the number still remaining rather than reporting a finished job. Refuses with 400 when no retention period is configured",
|
|
"example": "curl -X POST http://localhost:5001/api/auditlogs/purge -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"days\":365,\"dryrun\":true}'"
|
|
},
|
|
{
|
|
"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/<name>",
|
|
"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/<sso>",
|
|
"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/<int:sso>",
|
|
"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/<int:sso>",
|
|
"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=<csv> 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/<int:sso>/photo",
|
|
"purpose": "Upload or replace a self-hosted employee's photo (saved as photo-<sso><ext> 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=<image>, 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/<path:filename>",
|
|
"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/<int:sso>/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'"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/employees/resolve/<sso>",
|
|
"auth": "jwt",
|
|
"params": "sso in path",
|
|
"purpose": "Resolve one SSO to a name and photo, and say HOW it resolved or why it did not. Built because the failure is otherwise invisible: the shop-floor board resolves names through functions that returned None on any miss, so a directory problem and a typo looked identical",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/employees/resolve/123456789"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"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/<int:link_id>",
|
|
"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/<int:link_id>/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/<int:link_id>",
|
|
"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/<int:link_id>",
|
|
"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/<type_id>",
|
|
"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/<type_id>",
|
|
"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/<status_id>",
|
|
"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/<status_id>",
|
|
"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/<status_id>",
|
|
"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/<type_id>",
|
|
"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/<type_id>",
|
|
"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/<asset_id>",
|
|
"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/<asset_id>",
|
|
"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/<asset_id>",
|
|
"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/<assetnumber>",
|
|
"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/<asset_id>/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/<rel_id>",
|
|
"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/<asset_id>/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/<type_id>",
|
|
"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/<type_id>",
|
|
"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/<location_id>",
|
|
"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/<location_id>",
|
|
"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/<location_id>",
|
|
"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/<vendor_id>",
|
|
"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/<vendor_id>",
|
|
"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/<vendor_id>",
|
|
"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/<model_id>",
|
|
"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/<model_id>",
|
|
"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/<model_id>",
|
|
"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/<model_id>/image",
|
|
"purpose": "Upload/replace model photo (saved as instance/modelimages/model-<id><ext>, one per model); sets imageurl",
|
|
"auth": "admin",
|
|
"params": "multipart/form-data: file=<image> (.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/<filename>",
|
|
"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/<model_id>/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/<type_id>",
|
|
"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/<type_id>",
|
|
"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/<type_id>",
|
|
"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/<bu_id>",
|
|
"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/<bu_id>",
|
|
"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/<bu_id>",
|
|
"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/<os_id>",
|
|
"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/<os_id>",
|
|
"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/<os_id>",
|
|
"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/<fieldid>",
|
|
"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/<fieldid>",
|
|
"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/<assetid>",
|
|
"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/<assetid>",
|
|
"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/<team_id>",
|
|
"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/<team_id>",
|
|
"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/<team_id>",
|
|
"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/<team_id>/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/<team_id>/contacts/<contact_id>",
|
|
"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/<team_id>/contacts/<contact_id>",
|
|
"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/<app_id>",
|
|
"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/<app_id>",
|
|
"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/<app_id>",
|
|
"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/<app_id>/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/<app_id>/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/<app_id>/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/<machine_id>",
|
|
"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/<machine_id>",
|
|
"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/<machine_id>/<app_id>",
|
|
"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/<machine_id>/<app_id>",
|
|
"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}'"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/applications/<app_id>/image",
|
|
"auth": "jwt + applications.edit",
|
|
"params": "multipart/form-data: file=<image> (png/jpg/gif/webp/svg/ico)",
|
|
"purpose": "Upload or replace an application's image; sets application.image to the served URL",
|
|
"example": "curl -X POST -H 'Authorization: Bearer <token>' -F file=@logo.png http://localhost:5001/api/applications/5/image"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/applications/image/<filename>",
|
|
"auth": "none",
|
|
"params": "none",
|
|
"purpose": "Serve an uploaded application image. Public: application tiles render before login",
|
|
"example": "curl http://localhost:5001/api/applications/image/application-5.png"
|
|
},
|
|
{
|
|
"method": "DELETE",
|
|
"path": "/api/applications/<app_id>/image",
|
|
"auth": "jwt + applications.edit",
|
|
"params": "none",
|
|
"purpose": "Remove an application's image and clear the field",
|
|
"example": "curl -X DELETE -H 'Authorization: Bearer <token>' http://localhost:5001/api/applications/5/image"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/applications/<app_id>/package",
|
|
"auth": "jwt + applications.edit",
|
|
"params": "multipart/form-data: file=<installer> (exe/msi/msp/zip/7z/cab/iso/appx/msix/ps1/bat/txt/pdf), max 500MB",
|
|
"purpose": "Upload or replace the installer; sets installpath to the download URL",
|
|
"example": "curl -X POST -H 'Authorization: Bearer <token>' -F file=@setup.msi http://localhost:5001/api/applications/5/package"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/applications/package/<filename>",
|
|
"auth": "jwt + applications.view",
|
|
"params": "none",
|
|
"purpose": "Download an uploaded installer as an attachment. Authenticated: licensed vendor software",
|
|
"example": "curl -OJ -H 'Authorization: Bearer <token>' http://localhost:5001/api/applications/package/application-5.msi"
|
|
},
|
|
{
|
|
"method": "DELETE",
|
|
"path": "/api/applications/<app_id>/package",
|
|
"auth": "jwt + applications.edit",
|
|
"params": "none",
|
|
"purpose": "Remove an uploaded installer; clears installpath only when it pointed at the upload",
|
|
"example": "curl -X DELETE -H 'Authorization: Bearer <token>' http://localhost:5001/api/applications/5/package"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"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/<int:type_id>",
|
|
"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/<int:type_id>",
|
|
"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/<int:type_id>",
|
|
"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/<int:protocol_id>",
|
|
"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/<int:protocol_id>",
|
|
"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<serial>.<domain> 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/gaugelabreference/maintenancereference ilike), assetnumber (exact, for idempotent import), typeid|type_id, osid|os_id, locationid|location_id, businessunitid|businessunit_id, shopfloor (true/false), sort (hostname|assetnumber|name|lastreporteddate), dir (asc|desc)",
|
|
"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/<int:computer_id>",
|
|
"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/<int:asset_id>",
|
|
"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/<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/WKSTN0042'"
|
|
},
|
|
{
|
|
"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/<int:computer_id>",
|
|
"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/<int:computer_id>",
|
|
"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/<int:computer_id>/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/<int:computer_id>/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/<int:computer_id>/apps/<int:app_id>",
|
|
"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/<int:computer_id>/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\":\"jsmith\",\"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'"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/computers/dashboard/quiet",
|
|
"auth": "permission:computers.view",
|
|
"params": "none",
|
|
"purpose": "Dashboard card: PCs that WERE reporting, have stopped, and are still meant to be in use - a PC that never reported is not evidence of a fault",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/dashboard/quiet"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/computers/dashboard/sharedmachines",
|
|
"auth": "permission:computers.view",
|
|
"params": "none",
|
|
"purpose": "Dashboard card: machine numbers claimed by more than one PC, which is either a swap in progress or a mis-typed number",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/dashboard/sharedmachines"
|
|
},
|
|
{
|
|
"method": "PUT|PATCH",
|
|
"path": "/api/computers/protocols/<protocol_id>",
|
|
"auth": "permission:computers.edit (jwt_required)",
|
|
"params": "protocol_id in path; body: any of protocolname, port, isactive",
|
|
"purpose": "Update one remote-access protocol in the catalog (VNC, WinRM, RDP) that PCs report against",
|
|
"example": "curl -X PATCH -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"port\":5900}' http://localhost:5001/api/computers/protocols/3"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/computers/client-script",
|
|
"auth": "jwt + role:admin",
|
|
"params": "none",
|
|
"purpose": "The collector reporter (Report-AssetToShopDB.ps1) stamped with THIS site's values: the site base URL becomes the -ApiUrl default and computers_routableranges becomes -AllowedRanges, so it downloads ready to deploy. Only the parameter DEFAULTS are substituted - the copy in plugins/computers/client/ stays runnable, so there is no second version to drift - and everything stamped stays overridable by argument or registry. The collector key is deliberately NOT included: this file lands on every shop-floor PC, and a token spread across hundreds of bays cannot be rotated quietly. Serves text/plain as an attachment, with the SHA-256 in X-Script-Sha256 so a deployment can verify what it fetched",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/client-script"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/computers/policies/configuration",
|
|
"auth": "computers.view",
|
|
"params": "none",
|
|
"purpose": "List Configuration Policies (scope + the actions applied to matching endpoints); 404 while the computers_configpolicies_enabled setting is off",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/configuration"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/computers/policies/configuration/<policy_id>",
|
|
"auth": "computers.view",
|
|
"params": "path: policy_id",
|
|
"purpose": "One Configuration Policy with its actions expanded; 404 while the computers_configpolicies_enabled setting is off",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/configuration/1policy_id"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/computers/policies/configuration",
|
|
"auth": "computers.create",
|
|
"params": "body: name (required), description, scopejson (scope AST), actions[] of {action, mode: desired|gate, params}",
|
|
"purpose": "Create a Configuration Policy. Scope trees go through validate_scope and every action name is checked against the endpoints command allowlist, so a bad tree or unknown action is rejected before it reaches the database; 404 while the computers_configpolicies_enabled setting is off",
|
|
"example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"name\":\"Shopfloor MTConnect\",\"actions\":[{\"action\":\"mtconnect_start\",\"mode\":\"desired\"}]}' http://localhost:5001/api/computers/policies/configuration"
|
|
},
|
|
{
|
|
"method": "PUT",
|
|
"path": "/api/computers/policies/configuration/<policy_id>",
|
|
"auth": "computers.edit",
|
|
"params": "path: policy_id; body: any of name, description, scopejson, actions[] (actions replace wholesale)",
|
|
"purpose": "Update a Configuration Policy; same scope and action validation as create; 404 while the computers_configpolicies_enabled setting is off",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/configuration/1policy_id"
|
|
},
|
|
{
|
|
"method": "DELETE",
|
|
"path": "/api/computers/policies/configuration/<policy_id>",
|
|
"auth": "computers.delete",
|
|
"params": "path: policy_id",
|
|
"purpose": "Delete a Configuration Policy and its actions; 404 while the computers_configpolicies_enabled setting is off",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/configuration/1policy_id"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/computers/policies/business",
|
|
"auth": "computers.view",
|
|
"params": "none",
|
|
"purpose": "List Business Policies (ordered bundles of Configuration Policies); 404 while the computers_configpolicies_enabled setting is off",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/business"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/computers/policies/business/<policy_id>",
|
|
"auth": "computers.view",
|
|
"params": "path: policy_id",
|
|
"purpose": "One Business Policy with its member Configuration Policies in sort order; 404 while the computers_configpolicies_enabled setting is off",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/business/1policy_id"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/computers/policies/business",
|
|
"auth": "computers.create",
|
|
"params": "body: name (required), description, priority, items[] of {configurationpolicyid, sortorder}",
|
|
"purpose": "Create a Business Policy. Every referenced Configuration Policy must exist; priority decides which policy wins when two match the same endpoint; 404 while the computers_configpolicies_enabled setting is off",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/business"
|
|
},
|
|
{
|
|
"method": "PUT",
|
|
"path": "/api/computers/policies/business/<policy_id>",
|
|
"auth": "computers.edit",
|
|
"params": "path: policy_id; body: any of name, description, priority, items[] (items replace wholesale)",
|
|
"purpose": "Update a Business Policy or reorder its members; 404 while the computers_configpolicies_enabled setting is off",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/business/1policy_id"
|
|
},
|
|
{
|
|
"method": "DELETE",
|
|
"path": "/api/computers/policies/business/<policy_id>",
|
|
"auth": "computers.delete",
|
|
"params": "path: policy_id",
|
|
"purpose": "Delete a Business Policy and its item rows; 404 while the computers_configpolicies_enabled setting is off",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/business/1policy_id"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/computers/policies/actions",
|
|
"auth": "computers.view",
|
|
"params": "none",
|
|
"purpose": "The allowlisted action names and display labels a policy may reference, read from the endpoints command allowlist. The UI builds its action picker from this rather than hardcoding a list that would drift; 404 while the computers_configpolicies_enabled setting is off",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/actions"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/computers/policies/effective/<asset_id>",
|
|
"auth": "computers.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Preview the resolved effective policy for one endpoint: which Business Policy won, which Configuration Policies matched, and the resulting desired/gate actions. Read-only, for debugging a scope that is not matching what its author expected; 404 while the computers_configpolicies_enabled setting is off",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/policies/effective/1asset_id"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/computers/asset/<asset_id>/policypanel",
|
|
"auth": "computers.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Asset-detail panel: which policy governs this endpoint, how many configuration policies folded into it, and how many actions it allows or drives to a desired state. Provenance rather than a control - the effective policy narrows the command allowlist, so this is the answer to why an action is unavailable on a given PC. Returns an empty field list (not the feature-gate 404) when the policy feature is off, so the card self-hides instead of erroring on every site that has not enabled it",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/computers/asset/1/policypanel"
|
|
}
|
|
],
|
|
"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/<type_id>",
|
|
"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/<type_id>",
|
|
"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/<type_id>",
|
|
"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/gaugelabreference/maintenancereference 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/<tool_id>",
|
|
"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/<asset_id>",
|
|
"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/<tool_id>",
|
|
"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/<tool_id>",
|
|
"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/<type_id>",
|
|
"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/<type_id>",
|
|
"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/<type_id>",
|
|
"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/gaugelabreference/maintenancereference 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/<machine_id>",
|
|
"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/<asset_id>",
|
|
"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/<machine_id>",
|
|
"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/<machine_id>",
|
|
"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'"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/machines/dashboard/outofservice",
|
|
"auth": "permission:machines.view",
|
|
"params": "none",
|
|
"purpose": "Dashboard card: machines whose status says they are not running",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/machines/dashboard/outofservice"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"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/<sha256>",
|
|
"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\":\"WKSTN0042\",\"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/<int:scopeid>",
|
|
"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/<int:scopeid>",
|
|
"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/<int:scopeid>",
|
|
"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/<int:scopeid>/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/<int:scopeid>/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/<int:entryid>",
|
|
"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/<int:entryid>",
|
|
"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/<int:scopeid>/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/<int:scopeid>/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=WKSTN0042&cmmversion=2023.2'"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/geenforce/scopes/<int:scopeid>/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/<int:entryid>/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/<int:entryid>/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/<int:scopeid>/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/<int:scopeid>/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/<int:scopeid>/versions/<int:versionnumber>",
|
|
"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/<int:scopeid>/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/<int:scopeid>/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/<int:reportid>",
|
|
"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"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/geenforce/dashboard/failures",
|
|
"auth": "permission:geenforce.manage",
|
|
"params": "none",
|
|
"purpose": "Dashboard card: entries that FAILED on their PC's most recent enforcement cycle, host and entry named",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/geenforce/dashboard/failures"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/geenforce/scopes/<scopeid>/publish-preflight",
|
|
"auth": "permission:geenforce.manage",
|
|
"params": "scopeid in path",
|
|
"purpose": "Would publishing this scope outrun the fleet's enforcer library? Returns the required version, the lowest version reporting, the hosts behind it, and whether publish would be refused. The publish itself enforces the same gate; this is how the UI warns first",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/geenforce/scopes/3/publish-preflight"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"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/<int:type_id>",
|
|
"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/<int:notification_id>",
|
|
"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/<int:notification_id>",
|
|
"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/<int:notification_id>",
|
|
"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/<sso>",
|
|
"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'"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/notifications/dashboard/active",
|
|
"auth": "jwt",
|
|
"params": "none",
|
|
"purpose": "Dashboard card: the notifications currently showing on the shop-floor board, so the office can see what the floor sees",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/notifications/dashboard/active"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"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/<surface>/<path:filename>",
|
|
"purpose": "Public serving of a single slide image from instance/slides/<surface>/, 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/<surface>",
|
|
"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/<surface>/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/<surface>/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/<surface>/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/<surface>/<int:slideid>",
|
|
"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/<int:item_id>",
|
|
"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/<int:item_id>",
|
|
"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/<int:item_id>",
|
|
"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/<int:item_id>/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/<int:item_id>/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=<image>",
|
|
"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/<path:filename>",
|
|
"purpose": "Serve an uploaded item image from instance/printedpartsimages (public: fetched by <img> 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/<int:item_id>/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/<int:item_id>/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/<int:item_id>/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/<itemcode>",
|
|
"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/<int:item_id>/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/<int:item_id>/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=<print file> (required), note=<what changed> (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/<int:file_id>/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/<int:file_id>",
|
|
"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/<int:type_id>",
|
|
"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/<int:type_id>",
|
|
"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/<int:type_id>",
|
|
"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/gaugelabreference/maintenancereference), typeid|type_id, vendorid|vendor_id, locationid|location_id, businessunitid|businessunit_id, poe (true/false), managed (true/false), sort (hostname|assetnumber|name), dir (asc|desc)",
|
|
"example": "curl 'http://localhost:5001/api/network?typeid=2&poe=true&sort=hostname&dir=asc'"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/network/<int:device_id>",
|
|
"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/<int:asset_id>",
|
|
"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/<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/<int:device_id>",
|
|
"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/<int:device_id>",
|
|
"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/<int:vlan_id>",
|
|
"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/<int:vlan_id>",
|
|
"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/<int:vlan_id>",
|
|
"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/<int:subnet_id>",
|
|
"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/<int:subnet_id>",
|
|
"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/<int:subnet_id>",
|
|
"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/<device_id>",
|
|
"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/<device_id>",
|
|
"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/<device_id>/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/<device_id>/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/<device_id>/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/<device_id>/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\""
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/usb/kiosk/device/<device_id>",
|
|
"auth": "none (kiosk; requires usb_kiosk_enabled)",
|
|
"params": "path: device_id (the code on the drive label)",
|
|
"purpose": "What the logged-out kiosk shows after a drive is scanned: description, checked-in/out status, locker, and the NAME of the current holder (needed for the return-for-someone-else confirm; the holder's badge is never returned). 404 when the kiosk setting is off, so a disabled kiosk does not advertise the route",
|
|
"example": "curl http://localhost:5001/api/usb/kiosk/device/USB-1"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/usb/kiosk/checkout",
|
|
"auth": "none (kiosk; requires usb_kiosk_enabled)",
|
|
"params": "body: device_id, badge",
|
|
"purpose": "Check a drive out at the kiosk, attributed to the scanned badge. A badge that resolves to no employee is refused with 422 (unlike the signed-in route, which creates the user row). 409 when the drive is already out",
|
|
"example": "curl -X POST http://localhost:5001/api/usb/kiosk/checkout -H 'Content-Type: application/json' -d '{\"device_id\":\"USB-1\",\"badge\":\"270015376\"}'"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/usb/kiosk/checkin",
|
|
"auth": "none (kiosk; requires usb_kiosk_enabled)",
|
|
"params": "body: device_id, badge, forsomeoneelse (optional bool)",
|
|
"purpose": "Return a drive at the kiosk. Holder by default: a badge that does not match the current holder gets 409 with details.needsconfirm and the holder's name, and the caller re-posts with forsomeoneelse=true. The badge RECORDED is the one scanned, so the log shows who handed it back. Set usb_kiosk_allow_return_for_others=false to refuse a mismatch with 403 instead",
|
|
"example": "curl -X POST http://localhost:5001/api/usb/kiosk/checkin -H 'Content-Type: application/json' -d '{\"device_id\":\"USB-1\",\"badge\":\"270015376\"}'"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/usb/kiosk/session",
|
|
"auth": "none (kiosk; requires usb_kiosk_enabled)",
|
|
"params": "body: badge",
|
|
"purpose": "What one badge may do at the kiosk: the caller's own drives (to return), the drives that are free (to take), and a COUNT of everything else. The drives carry no barcode so the kiosk must offer a list; requiring a badge that resolves to a real person keeps that list from being anonymous, and a POST keeps the badge out of proxy access logs. 422 for an unresolvable badge",
|
|
"example": "curl -X POST http://localhost:5001/api/usb/kiosk/session -H 'Content-Type: application/json' -d '{\"badge\":\"270015376\"}'"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/usb/kiosk/others",
|
|
"auth": "none (kiosk; requires usb_kiosk_enabled)",
|
|
"params": "body: badge",
|
|
"purpose": "Drives checked out to OTHER people, with holder names, so somebody can return one left behind. Behind its own step rather than on the main kiosk screen, and 404 when usb_kiosk_allow_return_for_others is off - there is no point naming holders of drives the kiosk will not let anyone return",
|
|
"example": "curl -X POST http://localhost:5001/api/usb/kiosk/others -H 'Content-Type: application/json' -d '{\"badge\":\"270015376\"}'"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/usb/reports/audit",
|
|
"auth": "jwt + usb.view",
|
|
"params": "query: period (day|month|sixmonths|year|all, default month) or days=<n> (0 = all time); format=csv for a download",
|
|
"purpose": "Removable-media audit: every check-out and check-in in the window, newest first, with who, which drive, when and which locker - plus summary counts. Rows rather than totals, because an audit asks who had a specific drive on a specific day. Works against the external cmmc_usb log or the self-hosted tables; in self-hosted mode one stored loan expands into a take and a return so a returned drive is distinguishable from one still out",
|
|
"example": "curl 'http://localhost:5001/api/usb/reports/audit?period=year' -H \"Authorization: Bearer $TOKEN\""
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"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/<type_id>",
|
|
"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/<type_id>",
|
|
"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/<type_id>",
|
|
"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/<driver_id>",
|
|
"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/<driver_id>",
|
|
"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/gaugelabreference/maintenancereference), 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/for-host/<hostname>",
|
|
"auth": "jwt-optional",
|
|
"params": "path: hostname (matched case-insensitively, as sent by the client's COMPUTERNAME); no query parameters",
|
|
"purpose": "Desired printer set for one PC: its own usesprinter/defaultprinter relationships, or, when it has none, the ones inherited through its controls edge to the machine it drives. Each entry carries what a client needs to install the queue (queue name, hostname, ipaddress, port, drivername, driverlocation, isdefault, inherited). A known host with nothing assigned returns an empty list and a null default (the client's designed no-op); an unknown hostname, or a site without the computers plugin, is a 404.",
|
|
"example": "curl http://localhost:5001/api/printers/for-host/workstation01"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/printers/observed/<hostname>",
|
|
"auth": "permission:printers.view",
|
|
"params": "path: hostname (case-insensitive, short name or FQDN, as the reporting client sent it); no query parameters",
|
|
"purpose": "The observed half of the loop and the mirror of /for-host: what this bay last REPORTED it has (POST /api/collector/printers), each queue judged against the resolved assignment it SHOULD have. Read-only - nothing here changes an assignment, however wrong the bay looks. Every queue is classified matching / drifted (same printer, different port address or driver) / extra (a printer ShopDB knows, installed unassigned) / missing (assigned, not reported) / unknown (matches no printer, never guessed), each entry carrying both sides plus driftfields; the response adds observedat, the assignment source (own rows or the machine this PC controls), a per-classification summary, and a seedcandidate preview of what a seed would write. 404 only when the hostname has neither a report nor a computer record; a known PC that never reported returns an empty queue list.",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/observed/workstation01"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/printers/observed/for-asset/<asset_id>",
|
|
"auth": "permission:printers.view",
|
|
"params": "path: asset_id (a machine or a PC); no query parameters",
|
|
"purpose": "The same observed-against-assigned comparison as /api/printers/observed/<hostname>, reached from an asset page, with one block per reporting host. Assigned state lives on the MACHINE while observed state is reported by the PCs, so a machine answers with a block for each active PC that controls it (its own hostname first when the asset is itself a PC); blocks rather than one merged list because two PCs legitimately share one machine and the actionable part of drift is which box to walk to. Each block carries the same fields as the by-hostname route: queues classified matching / drifted / extra / missing / unknown, observedat, the assignment source, a summary, and a seedcandidate preview. Read-only. An asset nothing has reported for is an empty hosts list and a 200, not a 404.",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/observed/for-asset/312"
|
|
},
|
|
{
|
|
"method": "PUT",
|
|
"path": "/api/printers/assignments/for-asset/<asset_id>",
|
|
"auth": "permission:printers.edit",
|
|
"params": "path: asset_id (the machine or PC asset the printers belong to); body: printerassetids (list of printer asset IDs, required, empty list clears the assignment), defaultprinterassetid (int or null; 400 unless it is one of printerassetids)",
|
|
"purpose": "Reconcile an asset's whole printer assignment in one write: soft-deletes usesprinter rows that went away, reactivates previously removed ones, creates new ones, and replaces the single defaultprinter row (exactly one per asset, optional, always one of the assigned printers). Removing an assignment has no side effects and never uninstalls anything on a client.",
|
|
"example": "curl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"printerassetids\":[204,205],\"defaultprinterassetid\":204}' http://localhost:5001/api/printers/assignments/for-asset/312"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/printers/assignments/seed-from-observed/<asset_id>",
|
|
"auth": "permission:printers.edit",
|
|
"params": "path: asset_id (the machine or PC to write the assignment on); body, all optional: hostname (which report to seed from; omitted, the asset's own hostname or its single controlling PC), allowunmatched (bool, seed the matched queues anyway when some queue matches no printer). 404 when no named or derived host has reported; 409 when several controlling PCs have reported, or when any queue matches no printer and allowunmatched is unset; 400 when nothing matched, since writing the empty set would silently unassign the asset. Nothing is written on any of those.",
|
|
"purpose": "The ONE path from observed state to assigned state, and a person has to ask for it: no collector, cycle or background job reaches this route, so a bay that installed the wrong printer can never make itself right by reporting it. Writes the matched observed queues through the same reconcile as PUT /printers/assignments/for-asset (usesprinter rows plus the single defaultprinter, set only when the observed default itself matched a printer). Returns what was written, the skipped queues, and warnings - including one when the target controls another asset, because its own rows then shadow that machine's assignment for good. Normally posted against the MACHINE so the assignment survives a reimage.",
|
|
"example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"hostname\":\"workstation01\"}' http://localhost:5001/api/printers/assignments/seed-from-observed/312"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/printers/<printer_id>",
|
|
"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/<asset_id>",
|
|
"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/<printer_id>",
|
|
"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/<printer_id>",
|
|
"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/<printer_id>/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": "printers.view",
|
|
"params": "ip or fqdn (one required; ip matches a communication address, fqdn matches the printer's hostname)",
|
|
"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/<modelnumberid>/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/<modelnumberid>/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/<modelsupplyid>",
|
|
"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/<modelsupplyid>",
|
|
"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"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/printers/supplies/forecast",
|
|
"auth": "jwt-optional",
|
|
"params": "days (1-365, default 90)",
|
|
"purpose": "One row per CARTRIDGE: days-to-empty from Zabbix history (hourly trends over a long window), soonest first, banded empty/soon/month/later, each with its part numbers and replacement count. Also returns orderlist - what to buy within horizondays, grouped by part number with a quantity. Cartridges with no honest estimate come back separately with the reason. available=false when Zabbix is unreachable",
|
|
"example": "curl 'http://localhost:5001/api/printers/supplies/forecast?days=90'"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/printers/dashboard/supplies",
|
|
"auth": "permission:printers.view",
|
|
"params": "none",
|
|
"purpose": "Dashboard card: printers needing a cartridge, one row per printer. Reuses the low-supplies query and its five-minute cache, so the card costs the same as the report",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/dashboard/supplies"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/printers/assignments/for-asset/<asset_id>",
|
|
"auth": "optional jwt",
|
|
"params": "asset_id in path",
|
|
"purpose": "This asset's OWN printer assignment - printerassetids and defaultprinterassetid - deliberately WITHOUT inheritance. The editor has to show what this asset's own rows say, or a machine's printers would appear ticked on the PC that inherits them and unticking one would silently create an override. /printers/for-host is the resolved view the client uses; this is the editable one",
|
|
"example": "curl http://localhost:5001/api/printers/assignments/for-asset/14"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"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/<int:assetid>",
|
|
"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/<int:warrantyid>",
|
|
"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/<int:warrantyid>",
|
|
"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/<int:warrantyid>",
|
|
"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/<int:warrantyid>/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"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/warranty/<warrantyid>/proof",
|
|
"auth": "jwt + warranty.edit",
|
|
"params": "multipart/form-data: file=<document> (pdf/png/jpg/gif/webp/tif/msg/eml/doc/docx/xls/xlsx), max 25MB",
|
|
"purpose": "Upload or replace the proof-of-cover document (invoice, certificate)",
|
|
"example": "curl -X POST -H 'Authorization: Bearer <token>' -F file=@invoice.pdf http://localhost:5001/api/warranty/12/proof"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/warranty/proof/<filename>",
|
|
"auth": "jwt + warranty.view",
|
|
"params": "none",
|
|
"purpose": "Download a proof document under the vendor's original filename. Authenticated: carries pricing and a service tag",
|
|
"example": "curl -OJ -H 'Authorization: Bearer <token>' http://localhost:5001/api/warranty/proof/warranty-12.pdf"
|
|
},
|
|
{
|
|
"method": "DELETE",
|
|
"path": "/api/warranty/<warrantyid>/proof",
|
|
"auth": "jwt + warranty.edit",
|
|
"params": "none",
|
|
"purpose": "Remove a proof document and clear both columns",
|
|
"example": "curl -X DELETE -H 'Authorization: Bearer <token>' http://localhost:5001/api/warranty/12/proof"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/warranty/dashboard/expiring",
|
|
"auth": "permission:warranty.view",
|
|
"params": "none",
|
|
"purpose": "Dashboard card: warranties running out and ones that already have, identified the way the floor identifies them - by the PC's hostname and the machine it drives",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/warranty/dashboard/expiring"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"surface": "plugin-backups",
|
|
"endpoints": [
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/backups/kinds",
|
|
"auth": "permission:backups.view",
|
|
"params": "none",
|
|
"purpose": "Registered backup kinds (NTLARS/DNC, part marker, UDC, CMM ...) with each kind's storage backend and download formats, for UI that enumerates them rather than hardcoding the list",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/backups/kinds"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/backups/asset/<assetid>",
|
|
"auth": "permission:backups.view",
|
|
"params": "assetid in path; optional ?kind= to filter to one backup kind",
|
|
"purpose": "Revision history for one asset, newest first. An unchanged configuration writes no revision, so the newest row may be old and still current - each row carries both when it was captured and when it was last confirmed",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/backups/asset/42"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/backups/asset/<assetid>/info",
|
|
"auth": "permission:backups.view",
|
|
"params": "assetid in path; ?kind= selects the kind whose card is wanted",
|
|
"purpose": "A kind's at-a-glance card built from its latest revision, for the asset detail page",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" \"http://localhost:5001/api/backups/asset/42/info?kind=ntlars\""
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/backups/revisions/<backuprevisionid>",
|
|
"auth": "permission:backups.view",
|
|
"params": "backuprevisionid in path",
|
|
"purpose": "One revision, including its parsed payload where the kind stores one (opaque kinds return metadata only)",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/backups/revisions/1188"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/backups/revisions/<backuprevisionid>/diff",
|
|
"auth": "permission:backups.view",
|
|
"params": "backuprevisionid in path; ?against=<revisionid> to compare a specific pair, otherwise the previous revision of the same asset and kind",
|
|
"purpose": "Diff two revisions of the same asset and kind, key by key, which is what answers \"what changed on this machine\"",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/backups/revisions/1188/diff"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/backups/revisions/<backuprevisionid>/download",
|
|
"auth": "permission:backups.download",
|
|
"params": "backuprevisionid in path; ?format= one of the kind's formats (see /api/backups/kinds)",
|
|
"purpose": "Render a revision back to its native file format - for NTLARS, a .reg in either registry dialect, so it can be loaded straight back onto a controller PC. Returns the file, NOT the success envelope",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" -OJ \"http://localhost:5001/api/backups/revisions/1188/download?format=ntlars\""
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/backups/dashboard/stale",
|
|
"auth": "permission:backups.view",
|
|
"params": "none; the threshold is the backups_staledays setting, 0 disables the card",
|
|
"purpose": "Dashboard card (contract 0.19.0): chains whose backup has stopped running, judged on when a config was last CONFIRMED rather than last changed",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/backups/dashboard/stale"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"surface": "plugin-tools",
|
|
"endpoints": [
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/tools/secrets",
|
|
"auth": "permission:tools.secrets",
|
|
"params": "body: ciphertext (base64, max 4096 chars), iv, expiryhours (1-168, default 24), views (1-5, default 1)",
|
|
"purpose": "Store an already-encrypted secret and return its one-view link token. The AES key lives in the URL fragment and never reaches the server, so what is held here is ciphertext nobody on this side can read - which is why SIZE is the only limit the server can enforce, and why this is for passwords and short text rather than documents",
|
|
"example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" -d '{\"ciphertext\":\"...\",\"iv\":\"...\"}' http://localhost:5001/api/tools/secrets"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/tools/secrets/<token>",
|
|
"auth": "public",
|
|
"params": "none",
|
|
"purpose": "Redeem a secret: returns the ciphertext once and spends a view, deleting the row when the last view goes. POST rather than GET because chat and mail clients prefetch GET urls to build previews, and a preview must not consume a one-view secret. Missing, expired and spent answer identically so a guessed token learns nothing",
|
|
"example": "curl -X POST http://localhost:5001/api/tools/secrets/9f8e7d6c5b4a39281706f5e4d3c2b1a0"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/tools/secrets/<token>",
|
|
"auth": "public",
|
|
"params": "none",
|
|
"purpose": "Whether a link is still live, WITHOUT spending it. Lets the read page say \"a secret is waiting and opening it will destroy it\" instead of consuming the only view to find out, and gives link scanners something harmless to fetch",
|
|
"example": "curl http://localhost:5001/api/tools/secrets/9f8e7d6c5b4a39281706f5e4d3c2b1a0"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"surface": "core-docs",
|
|
"endpoints": [
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/docs/",
|
|
"auth": "none",
|
|
"params": "none",
|
|
"purpose": "Redoc page rendering the generated spec. Public, and asset URLs are built with url_for so it works under any mount path",
|
|
"example": "curl http://localhost:5001/api/docs/"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/docs/openapi.json",
|
|
"auth": "none",
|
|
"params": "none",
|
|
"purpose": "The generated OpenAPI 3.1 spec - the machine-readable description of this API, and what MCP and other clients build from. Returns the spec, NOT the success envelope",
|
|
"example": "curl http://localhost:5001/api/docs/openapi.json"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/docs/llms.txt",
|
|
"auth": "none",
|
|
"params": "none",
|
|
"purpose": "Concise orientation guide for an LLM or a new integrator: auth schemes, the response envelope, and the common recipes. Returns text/plain",
|
|
"example": "curl http://localhost:5001/api/docs/llms.txt"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/docs/redoc.standalone.js",
|
|
"auth": "none",
|
|
"params": "none",
|
|
"purpose": "Vendored Redoc bundle, served locally so the documentation page works on an air-gapped server",
|
|
"example": "curl -I http://localhost:5001/api/docs/redoc.standalone.js"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"surface": "core-map",
|
|
"endpoints": [
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/maplevels",
|
|
"auth": "none",
|
|
"params": "none",
|
|
"purpose": "Every building with its levels in display order, each carrying blueprint paths, native pixel size and marker count, plus defaultlevelid. PUBLIC: the printer installer map draws a blueprint before anyone logs in",
|
|
"example": "curl http://localhost:5001/api/maplevels"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/maplevels/<levelid>",
|
|
"auth": "none",
|
|
"params": "levelid in path",
|
|
"purpose": "One level: name, building, blueprints and native size. That size is what mapx/mapy on this level are pixels of (ADR-017)",
|
|
"example": "curl http://localhost:5001/api/maplevels/2"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/maplevels/<levelid>/blueprint/<filename>",
|
|
"auth": "none",
|
|
"params": "levelid and filename in path",
|
|
"purpose": "Serve a level's blueprint image, with sandbox headers so an SVG floor plan cannot execute as script",
|
|
"example": "curl -I http://localhost:5001/api/maplevels/2/blueprint/level-2-light.png"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/maplevels/buildings",
|
|
"auth": "jwt + role:admin",
|
|
"params": "body: buildingname (required), sortorder",
|
|
"purpose": "Create a building. An asset references the level, never the building, so the two cannot disagree",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"buildingname\":\"Annex\"}' -X POST http://localhost:5001/api/maplevels/buildings"
|
|
},
|
|
{
|
|
"method": "PUT|PATCH",
|
|
"path": "/api/maplevels/buildings/<buildingid>",
|
|
"auth": "jwt + role:admin",
|
|
"params": "buildingid in path; body: buildingname, sortorder, isactive",
|
|
"purpose": "Rename or reorder a building. Levels move with it and nothing repositions",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" -X PATCH http://localhost:5001/api/maplevels/buildings/1"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/maplevels",
|
|
"auth": "jwt + role:admin",
|
|
"params": "body: buildingid and levelname (required), sortorder, blueprintlight, blueprintdark, mapwidth, mapheight, isdefault",
|
|
"purpose": "Create a level. Name and sort order are separate because levels are not reliably numbered (basement, mezzanine, roof), and gaps let one be inserted later without renumbering",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" -X POST http://localhost:5001/api/maplevels"
|
|
},
|
|
{
|
|
"method": "PUT|PATCH",
|
|
"path": "/api/maplevels/<levelid>",
|
|
"auth": "jwt + role:admin",
|
|
"params": "levelid in path; body: levelname, sortorder, blueprintlight, blueprintdark, mapwidth, mapheight, isdefault, isactive",
|
|
"purpose": "Update a level. Changing mapwidth/mapheight returns a warning naming what it affects: the dimensions are the coordinate space every marker is expressed in, so a resize moves them all relative to the drawing - use a landmark transform instead",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" -X PATCH http://localhost:5001/api/maplevels/1"
|
|
},
|
|
{
|
|
"method": "DELETE",
|
|
"path": "/api/maplevels/<levelid>",
|
|
"auth": "jwt + role:admin",
|
|
"params": "levelid in path",
|
|
"purpose": "Deactivate a level. Refused with 409 while assets are placed on it, and refused for the default level: deleting a drawing out from under a marker leaves a position in a coordinate space that no longer exists",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" -X DELETE http://localhost:5001/api/maplevels/3"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/maplevels/<levelid>/blueprint",
|
|
"auth": "jwt + role:admin",
|
|
"params": "levelid in path; multipart/form-data: file=<image>, theme=light|dark",
|
|
"purpose": "Upload a level's blueprint. Reads the image's real pixel size from its header and adopts it when the level is EMPTY; with markers already placed it reports the mismatch and changes nothing, because adopting a new coordinate space silently moves every marker while looking like a successful upload",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" -F file=@second-floor.png -F theme=light -X POST http://localhost:5001/api/maplevels/2/blueprint"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/mappositions/transform",
|
|
"auth": "permission:assets.edit",
|
|
"params": "body: levelid (required), landmarks [{fromx,fromy,tox,toy}] (two or more), tolevelid, assetids, dryrun (defaults TRUE)",
|
|
"purpose": "Move every placed marker on a level by a transform derived per axis from landmark pairs. NEVER from image dimensions: a level added below another changes canvas height without rescaling anything, and a dimension-derived scale would stretch Y and be wrong everywhere. Dry run returns every old and new position plus which land outside the target. Applying snapshots first and clears mapverifiedat, because a transformed position is a guess awaiting review",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"levelid\":1,\"landmarks\":[{\"fromx\":0,\"fromy\":0,\"tox\":0,\"toy\":1450},{\"fromx\":1000,\"fromy\":1000,\"tox\":1000,\"toy\":2450}]}' -X POST http://localhost:5001/api/mappositions/transform"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/mappositions/positions",
|
|
"auth": "permission:assets.edit",
|
|
"params": "body: positions [{assetid, mapx, mapy, levelid}] - levelid required per row - and verified",
|
|
"purpose": "Set many positions at once, snapshotting first. levelid is per position rather than per request because a bulk save can span levels and inferring it is the guess this model exists to remove. Placing by hand counts as review, so mapverifiedat is stamped",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"positions\":[{\"assetid\":42,\"mapx\":1200,\"mapy\":900,\"levelid\":2}]}' -X POST http://localhost:5001/api/mappositions/positions"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/mappositions/verify",
|
|
"auth": "permission:assets.edit",
|
|
"params": "body: assetids (required), unverify",
|
|
"purpose": "Mark markers reviewed against the current drawing without moving them - the common case in a review pass. No snapshot, because no position changes",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"assetids\":[42,43]}' -X POST http://localhost:5001/api/mappositions/verify"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/mappositions/snapshots",
|
|
"auth": "permission:assets.view",
|
|
"params": "none; newest 50",
|
|
"purpose": "Position snapshots, newest first, with what caused each and whether it has been restored. Metadata only - the positions are large",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/mappositions/snapshots"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/mappositions/snapshots/<snapshotid>/restore",
|
|
"auth": "permission:assets.edit",
|
|
"params": "snapshotid in path",
|
|
"purpose": "Put a snapshot back, snapshotting first so an undo is itself undoable. Restores level and review state, not just coordinates, and reports assets that no longer exist rather than failing the whole restore",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" -X POST http://localhost:5001/api/mappositions/snapshots/7/restore"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"surface": "guides",
|
|
"endpoints": [
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/guides",
|
|
"auth": "jwt + guides.view",
|
|
"params": "query: q (searches title, summary, tags and body), tag",
|
|
"purpose": "List guides. Published guides plus the caller's own drafts; someone with guides.edit sees every draft",
|
|
"example": "curl 'http://localhost:5001/api/guides?tag=partmarker' -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/guides/tags",
|
|
"auth": "jwt + guides.view",
|
|
"params": "none",
|
|
"purpose": "Every tag in use with a count, for filter chips",
|
|
"example": "curl http://localhost:5001/api/guides/tags -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/guides/<slug>",
|
|
"auth": "jwt + guides.view",
|
|
"params": "none",
|
|
"purpose": "Read a guide as a READER: returns the PUBLISHED revision, not the working copy, so an edit in progress does not change what somebody following the procedure sees. 404 (not 403) for an unpublished guide the caller cannot edit",
|
|
"example": "curl http://localhost:5001/api/guides/replace-a-marker -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/guides/<guideid>/working",
|
|
"auth": "jwt + guides.view (+ edit rights)",
|
|
"params": "none",
|
|
"purpose": "The working copy, for the editor. Requires guides.edit, or guides.create while the guide is your own and unpublished",
|
|
"example": "curl http://localhost:5001/api/guides/12/working -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/guides/<guideid>/revisions",
|
|
"auth": "jwt + guides.view",
|
|
"params": "none",
|
|
"purpose": "Version history, newest first, flagging which revision is published",
|
|
"example": "curl http://localhost:5001/api/guides/12/revisions -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/guides/revisions/<revisionid>",
|
|
"auth": "jwt + guides.view",
|
|
"params": "none",
|
|
"purpose": "One revision including its blocks, for viewing or comparing an old version",
|
|
"example": "curl http://localhost:5001/api/guides/revisions/34 -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/guides",
|
|
"auth": "jwt + guides.create",
|
|
"params": "body: title (required), summary, tags (comma separated), blocks (array), note",
|
|
"purpose": "Create a guide as a draft. Every block needs a unique, stable id: the id is what a reader's tick is remembered against, and a save without one is refused",
|
|
"example": "curl -X POST http://localhost:5001/api/guides -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"title\":\"Swap a controller\",\"tags\":\"partmarker\",\"blocks\":[{\"id\":\"b1\",\"type\":\"paragraph\",\"text\":\"Power it off.\"}]}'"
|
|
},
|
|
{
|
|
"method": "PUT",
|
|
"path": "/api/guides/<guideid>",
|
|
"auth": "jwt + guides.view (+ edit rights)",
|
|
"params": "body: any of title, summary, tags, blocks, note",
|
|
"purpose": "Save the working copy. Writes a new revision ONLY when the content hash changes, so an autosave that changed nothing records nothing; the response carries saved=false in that case",
|
|
"example": "curl -X PUT http://localhost:5001/api/guides/12 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"blocks\":[]}'"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/guides/<guideid>/publish",
|
|
"auth": "jwt + guides.publish",
|
|
"params": "none",
|
|
"purpose": "Point readers at the latest saved revision. Promotes an existing revision rather than snapshotting, so a publish cannot ship unsaved edits nobody reviewed",
|
|
"example": "curl -X POST http://localhost:5001/api/guides/12/publish -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/guides/<guideid>/unpublish",
|
|
"auth": "jwt + guides.publish",
|
|
"params": "none",
|
|
"purpose": "Withdraw from readers without deleting anything; revisions survive so the guide can be corrected and published again",
|
|
"example": "curl -X POST http://localhost:5001/api/guides/12/unpublish -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/guides/<guideid>/restore/<revisionid>",
|
|
"auth": "jwt + guides.view (+ edit rights)",
|
|
"params": "none",
|
|
"purpose": "Bring an old revision back as a NEW revision. History is never rewritten, so what was published in between stays on record",
|
|
"example": "curl -X POST http://localhost:5001/api/guides/12/restore/34 -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"method": "DELETE",
|
|
"path": "/api/guides/<guideid>",
|
|
"auth": "jwt + guides.delete",
|
|
"params": "none",
|
|
"purpose": "Delete a guide and its revisions and images",
|
|
"example": "curl -X DELETE http://localhost:5001/api/guides/12 -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/guides/images",
|
|
"auth": "jwt + guides.create",
|
|
"params": "multipart: file (png/jpeg/gif/webp, max 8 MB), guideid (optional)",
|
|
"purpose": "Upload an image for a guide. guideid is optional because an image is pasted before a new guide has an id; the editor attaches it on save",
|
|
"example": "curl -X POST http://localhost:5001/api/guides/images -H \"Authorization: Bearer $TOK\" -F file=@screenshot.png -F guideid=12"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/guides/images/<imageid>",
|
|
"auth": "jwt + guides.view",
|
|
"params": "none",
|
|
"purpose": "Serve an uploaded image; cached immutably since an image is replaced rather than edited",
|
|
"example": "curl http://localhost:5001/api/guides/images/5 -H \"Authorization: Bearer $TOK\" --output shot.png"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"surface": "plugin-endpoints",
|
|
"endpoints": [
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/",
|
|
"auth": "endpoints.view",
|
|
"params": "query: search (hostname substring)",
|
|
"purpose": "List endpoint states. Registered both with and without the trailing slash, so both forms are described here",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/<asset_id>",
|
|
"auth": "endpoints.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "One endpoint: its agent state, version, and last check-in",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/1asset_id"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/<asset_id>/events",
|
|
"auth": "endpoints.view",
|
|
"params": "path: asset_id; query: eventid (page from this id)",
|
|
"purpose": "Agent event history for one endpoint, newest first",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/1asset_id/events"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/asset/<asset_id>/status",
|
|
"auth": "endpoints.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Asset-detail panel: online state, uptime, and agent version for one endpoint",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1asset_id/status"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/asset/<asset_id>/telemetrymeta",
|
|
"auth": "endpoints.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Asset-detail panel: what telemetry this endpoint reports and when each series was last updated",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1asset_id/telemetrymeta"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/asset/<asset_id>/services",
|
|
"auth": "endpoints.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Asset-detail panel: Windows services the agent watches on this endpoint, with running state",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1asset_id/services"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/asset/<asset_id>/recentevents",
|
|
"auth": "endpoints.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Asset-detail panel: the most recent agent events for one endpoint",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1asset_id/recentevents"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/asset/<asset_id>/activity",
|
|
"auth": "endpoints.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Asset-detail panel: one chronological feed merging curated Windows events with operator commands, newest first, capped at 15. Both are keyed by assetid, so this is two indexed reads and a merge rather than a join. A failed command is marked critical. The requester name is withheld from callers who do not themselves hold endpoints.command, so a read-only viewer gets the history without the roster of who restarted what",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/activity"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/asset/<asset_id>/interfaces",
|
|
"auth": "endpoints.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Asset-detail panel: network interfaces as the collector reports them ({alias, ipv4, mac, gateway, subnet, dhcp})",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1asset_id/interfaces"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/dashboard/summary",
|
|
"auth": "endpoints.view",
|
|
"params": "none",
|
|
"purpose": "Dashboard card: endpoint counts by online/offline/never-seen",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/dashboard/summary"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/dashboard/quiet",
|
|
"auth": "endpoints.view",
|
|
"params": "none",
|
|
"purpose": "Dashboard card: endpoints that have gone quiet - no check-in inside the configured quiet-hours window",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/dashboard/quiet"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/dashboard/stability",
|
|
"auth": "endpoints.view",
|
|
"params": "none",
|
|
"purpose": "Dashboard card: endpoints ranked by reboot/agent-restart churn, to surface a machine that is flapping",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/dashboard/stability"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/agentkey",
|
|
"auth": "admin role",
|
|
"params": "none",
|
|
"purpose": "Status of the endpoints agent key: whether one exists, when it was issued, and its last use. Never returns the key itself",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/agentkey"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/endpoints/agentkey/regenerate",
|
|
"auth": "admin role",
|
|
"params": "none",
|
|
"purpose": "Issue a new endpoints agent key and return it ONCE. The key is scoped to collector.ingest.endpoints only, so rotating it cannot disturb the computers or GE-Enforce collectors. Every agent must be reconfigured with the new value",
|
|
"example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/agentkey/regenerate"
|
|
},
|
|
{
|
|
"method": "DELETE",
|
|
"path": "/api/endpoints/agentkey",
|
|
"auth": "admin role",
|
|
"params": "none",
|
|
"purpose": "Revoke the endpoints agent key. The kill switch for agent ingest: reports and command polls stop being authorized immediately",
|
|
"example": "curl -X DELETE -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/agentkey"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/<asset_id>/commands",
|
|
"auth": "endpoints.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Command history for one endpoint: what was queued, claimed, and what it returned",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/1asset_id/commands"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/endpoints/<asset_id>/commands",
|
|
"auth": "endpoints.command",
|
|
"params": "path: asset_id; body: action (required, allowlisted), target, targetkind",
|
|
"purpose": "Queue one allowlisted command for an endpoint. Unknown actions are rejected, destructive ones (system_restart) demand a stricter permission, the effective policy must permit the action for this endpoint, and a second command will not stack while one is pending or running",
|
|
"example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"action\":\"service_restart\",\"target\":\"MTConnectAgent\",\"targetkind\":\"service\"}' http://localhost:5001/api/endpoints/1/commands"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/commands/next",
|
|
"auth": "collector token (collector.ingest.endpoints)",
|
|
"params": "query: hostname (required)",
|
|
"purpose": "Agent long-poll. Claims the oldest pending command for the calling host and atomically marks it running so no second poll claims it; 204 when nothing is queued inside the hold window, and the agent re-polls. Collector-token auth, not JWT",
|
|
"example": "curl -H \"X-Collector-Key: $KEY\" \"http://localhost:5001/api/endpoints/commands/next?hostname=WJSF-PC-01\""
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/endpoints/commands/<command_id>/result",
|
|
"auth": "collector token (collector.ingest.endpoints)",
|
|
"params": "path: command_id; body: the command outcome (status, output)",
|
|
"purpose": "Agent reports a claimed command result. Finalizes the command row and, for MTConnect actions that observed run state, caches that on the EndpointState so the UI toggle reflects reality between polls - both in one commit. Collector-token auth, not JWT",
|
|
"example": "curl -X POST -H \"X-Collector-Key: $KEY\" -H 'Content-Type: application/json' -d '{\"status\":\"succeeded\",\"output\":\"ok\"}' http://localhost:5001/api/endpoints/commands/1/result"
|
|
},
|
|
{
|
|
"path": "/api/endpoints/monitors",
|
|
"method": "GET",
|
|
"auth": "jwt + require_permission endpoints.view",
|
|
"params": "none",
|
|
"purpose": "List every monitor definition, shipped and local, ordered shipped-first then by display name",
|
|
"example": "curl http://localhost:5001/api/endpoints/monitors -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"path": "/api/endpoints/monitors",
|
|
"method": "POST",
|
|
"auth": "jwt + require_role admin",
|
|
"params": "body: kind (process|service, required), displayname (required), processname (required for process), servicename (required for service), launchpath, datacollectorpath, logcollectorpath, isactive (default true)",
|
|
"purpose": "Create a local monitor definition; source is forced to 'local' so a caller cannot mint a shipped row",
|
|
"example": "curl -X POST http://localhost:5001/api/endpoints/monitors -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"kind\":\"service\",\"displayname\":\"Spooler\",\"servicename\":\"Spooler\"}'"
|
|
},
|
|
{
|
|
"path": "/api/endpoints/monitors/<monitor_id>",
|
|
"method": "PUT",
|
|
"auth": "jwt + require_role admin",
|
|
"params": "path: monitor_id; body: same fields as create, all optional",
|
|
"purpose": "Update a local monitor definition; 403 on a shipped row, which is product-owned and read-only",
|
|
"example": "curl -X PUT http://localhost:5001/api/endpoints/monitors/4 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"isactive\":false}'"
|
|
},
|
|
{
|
|
"path": "/api/endpoints/monitors/<monitor_id>",
|
|
"method": "DELETE",
|
|
"auth": "jwt + require_role admin",
|
|
"params": "path: monitor_id",
|
|
"purpose": "Delete a local monitor definition; 403 on a shipped row",
|
|
"example": "curl -X DELETE http://localhost:5001/api/endpoints/monitors/4 -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"path": "/api/endpoints/monitors/resolve",
|
|
"method": "GET",
|
|
"auth": "jwt + require_permission endpoints.view",
|
|
"params": "query: pctype (required)",
|
|
"purpose": "Resolve the monitor set a PC type should watch: shipped defaults for 'Shopfloor PC' plus whatever active site policy attaches to that type. The human-facing view of what the agent will be told",
|
|
"example": "curl 'http://localhost:5001/api/endpoints/monitors/resolve?pctype=Shopfloor+PC' -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"path": "/api/endpoints/monitors/resolved",
|
|
"method": "GET",
|
|
"auth": "endpoints collector API key (X-API-Key/Bearer), not JWT",
|
|
"params": "query: hostname (required)",
|
|
"purpose": "Agent runtime watch-list. The agent calls this each telemetry cycle and receives {processes[], services[]} shaped like its own MonitorConfig, so a policy-attached monitor is watched without editing appsettings.json on the bay. Unknown hostname is a 404; a PC type with no monitors returns empty lists",
|
|
"example": "curl 'http://localhost:5001/api/endpoints/monitors/resolved?hostname=F7MHPF24' -H \"X-API-Key: $ENDPOINTS_KEY\""
|
|
},
|
|
{
|
|
"path": "/api/endpoints/operatorreports",
|
|
"method": "POST",
|
|
"auth": "endpoints collector API key (X-API-Key/Bearer), not JWT",
|
|
"params": "multipart/form-data: hostname (required), reason (default 'Other (free text)', truncated to 128), details, screenshot (png, 10 MB cap)",
|
|
"purpose": "Agent uploads one operator issue report captured from the tray. The hostname must resolve to a computer asset. The screenshot is stored under a server-generated uuid filename; a retention sweep runs opportunistically so the feed stays bounded without a scheduler",
|
|
"example": "curl -X POST http://localhost:5001/api/endpoints/operatorreports -H \"X-API-Key: $ENDPOINTS_KEY\" -F hostname=F7MHPF24 -F 'details=Machine will not home' -F screenshot=@shot.png"
|
|
},
|
|
{
|
|
"path": "/api/endpoints/panel/<asset_id>/operatorreports",
|
|
"method": "GET",
|
|
"auth": "jwt + require_permission endpoints.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Operator Reports asset panel: this endpoint's reports newest first, each carrying a hasimage flag so the viewer knows whether to request the screenshot",
|
|
"example": "curl http://localhost:5001/api/endpoints/panel/46/operatorreports -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"path": "/api/endpoints/operatorreports/<operatorreportid>/image",
|
|
"method": "GET",
|
|
"auth": "jwt + require_permission endpoints.view",
|
|
"params": "path: operatorreportid",
|
|
"purpose": "Serve one operator report's screenshot, hardened with the shared upload headers (CSP sandbox + nosniff); 404 when the report carries no image",
|
|
"example": "curl http://localhost:5001/api/endpoints/operatorreports/12/image -H \"Authorization: Bearer $TOK\" -o shot.png"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints",
|
|
"auth": "endpoints.view",
|
|
"params": "query: search (hostname substring)",
|
|
"purpose": "List endpoint states. Registered both with and without the trailing slash, so both forms are described here",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/asset/<asset_id>/hardware",
|
|
"auth": "endpoints.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Asset-detail panel: processor, memory and chassis inventory. Agent 1.7.0 and later only; an older agent leaves the columns null and the panel hides itself rather than rendering blanks",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/hardware"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/asset/<asset_id>/osinfo",
|
|
"auth": "endpoints.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Asset-detail panel: operating system, firmware and domain membership as one card (both answer \"what is this box running\")",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/osinfo"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/asset/<asset_id>/storage",
|
|
"auth": "endpoints.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Asset-detail panel: volume capacity and physical drive health as two sections. They share no key (volumes vs physical devices) and are never reconciled across sections",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/storage"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/asset/<asset_id>/diskhealth",
|
|
"auth": "endpoints.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Physical drive health rows, anything predicting failure sorted first. No longer backs a panel of its own (Storage renders it as a section) but kept as a stable read for callers outside the panel renderer",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/diskhealth"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/asset/<asset_id>/listeners",
|
|
"auth": "endpoints.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Asset-detail panel: TCP sockets in LISTEN with the owning process, sorted by port. The full connection table is an on-demand command, not standard telemetry",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/listeners"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/asset/<asset_id>/mappeddrives",
|
|
"auth": "endpoints.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Asset-detail panel: mapped network drives with a health badge each. Session-scoped, so an empty list when nobody is signed in is a valid state, not a failure",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/mappeddrives"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/asset/<asset_id>/usercontext",
|
|
"auth": "endpoints.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Asset-detail panel: session-scoped data from the interactive user probe. Mapped drives arrive from two sources that can disagree and the worse status wins, so a half-broken share is never shown healthy",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/usercontext"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/asset/<asset_id>/iphistory",
|
|
"auth": "endpoints.view",
|
|
"params": "path: asset_id",
|
|
"purpose": "Asset-detail panel: every address this asset has held, newest change first. \"Last changed\" is when a field on the row moved, NOT when the address was last seen",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/endpoints/asset/1/iphistory"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"surface": "core-scripts",
|
|
"_comment": "Script library: reusable PowerShell bodies a run_script delivery pushes to an endpoint. Added on the wilm branch; documented here on merge because docs/ is not published, so an external contributor cannot maintain this file.",
|
|
"endpoints": [
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/scripts",
|
|
"auth": "jwt + require_permission applications.view",
|
|
"params": "query: includeinactive (default false)",
|
|
"purpose": "List script catalog entries, active only by default",
|
|
"example": "curl http://localhost:5001/api/scripts -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/scripts/<script_id>",
|
|
"auth": "jwt + require_permission applications.view",
|
|
"params": "path: script_id (int)",
|
|
"purpose": "Get one script entry with its metadata and run context",
|
|
"example": "curl http://localhost:5001/api/scripts/3 -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/scripts",
|
|
"auth": "jwt + require_permission applications.create",
|
|
"params": "body: name (required), description, runcontext (system|user, default system)",
|
|
"purpose": "Create a script catalog entry; the body is uploaded separately so metadata and content version independently",
|
|
"example": "curl -X POST http://localhost:5001/api/scripts -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"name\":\"Restart spooler\",\"runcontext\":\"system\"}'"
|
|
},
|
|
{
|
|
"method": "PUT",
|
|
"path": "/api/scripts/<script_id>",
|
|
"auth": "jwt + require_permission applications.edit",
|
|
"params": "path: script_id; body: name, description, runcontext, isactive",
|
|
"purpose": "Update a script entry's metadata; the stored body is untouched",
|
|
"example": "curl -X PUT http://localhost:5001/api/scripts/3 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"description\":\"Clears the print queue\"}'"
|
|
},
|
|
{
|
|
"method": "DELETE",
|
|
"path": "/api/scripts/<script_id>",
|
|
"auth": "jwt + require_permission applications.delete",
|
|
"params": "path: script_id",
|
|
"purpose": "Remove a script catalog entry",
|
|
"example": "curl -X DELETE http://localhost:5001/api/scripts/3 -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/scripts/<script_id>/body",
|
|
"auth": "jwt + require_permission applications.edit",
|
|
"params": "path: script_id; multipart/form-data: file (the .ps1)",
|
|
"purpose": "Upload or replace the script body; stored on disk and hashed so a delivery can pin the exact bytes",
|
|
"example": "curl -X POST http://localhost:5001/api/scripts/3/body -H \"Authorization: Bearer $TOK\" -F 'file=@restart-spooler.ps1'"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/scripts/body/<filename>",
|
|
"auth": "jwt + require_permission applications.view",
|
|
"params": "path: filename (as stored)",
|
|
"purpose": "Download a stored script body for review in the UI",
|
|
"example": "curl http://localhost:5001/api/scripts/body/restart-spooler.ps1 -H \"Authorization: Bearer $TOK\" -o restart-spooler.ps1"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"surface": "endpoints-delivery",
|
|
"_comment": "Agent-facing software delivery. These authorise on the endpoints COLLECTOR TOKEN, not a user JWT: the agent already holds that key for telemetry and command polling, and the core package route is gated on applications.view which the collector token does not hold.",
|
|
"endpoints": [
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/apppackage/<appversionid>",
|
|
"auth": "collector token (X-API-Key), endpoints scope",
|
|
"params": "path: appversionid (int)",
|
|
"purpose": "Agent-facing installer download for a pinned app version; serves the exact bytes the update_app descriptor pinned by SHA-256",
|
|
"example": "curl http://localhost:5001/api/endpoints/apppackage/12 -H 'X-API-Key: $COLLECTOR_KEY' -o setup.exe"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/apppackage/app/<app_id>",
|
|
"auth": "collector token (X-API-Key), endpoints scope",
|
|
"params": "path: app_id (int)",
|
|
"purpose": "Agent-facing installer download resolved from the application rather than a specific version",
|
|
"example": "curl http://localhost:5001/api/endpoints/apppackage/app/4 -H 'X-API-Key: $COLLECTOR_KEY' -o setup.exe"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/apppackage/app/<app_id>/<filename>",
|
|
"auth": "collector token (X-API-Key), endpoints scope",
|
|
"params": "path: app_id (int), filename",
|
|
"purpose": "As above, naming the stored file explicitly so the agent writes it back under the expected name",
|
|
"example": "curl http://localhost:5001/api/endpoints/apppackage/app/4/setup.exe -H 'X-API-Key: $COLLECTOR_KEY' -o setup.exe"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/scriptbody/<script_id>",
|
|
"auth": "collector token (X-API-Key), endpoints scope",
|
|
"params": "path: script_id (int)",
|
|
"purpose": "Agent-facing script download for a run_script delivery, on the same key as the rest of the delivery path",
|
|
"example": "curl http://localhost:5001/api/endpoints/scriptbody/3 -H 'X-API-Key: $COLLECTOR_KEY' -o run.ps1"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/asset/<asset_id>/applications",
|
|
"auth": "jwt + require_permission endpoints.view",
|
|
"params": "path: asset_id (int)",
|
|
"purpose": "Installed-software snapshot the agent last reported for this endpoint, for the asset detail panel; a per-report snapshot rather than history",
|
|
"example": "curl http://localhost:5001/api/endpoints/asset/313/applications -H \"Authorization: Bearer $TOK\""
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"surface": "endpoints-siteagent",
|
|
"_comment": "Packaging the endpoint agent and the agentless adoption tool for this site.",
|
|
"endpoints": [
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/endpoints/siteagent",
|
|
"auth": "jwt + require_role admin",
|
|
"params": "none",
|
|
"purpose": "Report whether a prebuilt agent binary is staged and can therefore be packaged",
|
|
"example": "curl http://localhost:5001/api/endpoints/siteagent -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/endpoints/siteagent/package",
|
|
"auth": "jwt + require_role admin",
|
|
"params": "body: baseurl, collectorkey, cadenceseconds, and the site TLS cert when one is readable",
|
|
"purpose": "Bundle the agent as a downloadable ZIP with a generated install.ps1 that provisions credentials, so a bay install needs no hand editing",
|
|
"example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/package -H \"Authorization: Bearer $TOK\" -o shopdb-agent.zip"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/endpoints/adoptiontool/package",
|
|
"auth": "jwt + require_role admin",
|
|
"params": "body: as for the site agent package",
|
|
"purpose": "Bundle the agentless adoption tool, for taking an inventory of a machine that will not run the service",
|
|
"example": "curl -X POST http://localhost:5001/api/endpoints/adoptiontool/package -H \"Authorization: Bearer $TOK\" -o shopdb-adoption.zip"
|
|
},
|
|
{
|
|
"path": "/api/endpoints/siteagent/binaries/<binkey>",
|
|
"method": "GET",
|
|
"auth": "jwt + require_permission endpoints.agentpackage",
|
|
"params": "path: binkey (agent|tray)",
|
|
"purpose": "List the staged versions of one agent binary, newest first, with the current one marked",
|
|
"example": "curl http://localhost:5001/api/endpoints/siteagent/binaries/agent -H \"Authorization: Bearer $TOK\""
|
|
},
|
|
{
|
|
"path": "/api/endpoints/siteagent/binaries/<binkey>",
|
|
"method": "POST",
|
|
"auth": "jwt + require_permission endpoints.agentpackage",
|
|
"params": "path: binkey (agent|tray); multipart/form-data: file (a Windows PE, 64 MB cap), version (strict dotted-numeric), makecurrent (default true)",
|
|
"purpose": "Stage an uploaded binary as a new version. This supplies the executable every future package installs and runs as SYSTEM on the fleet, so it is audit-logged with the stored file's sha256",
|
|
"example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/agent -H \"Authorization: Bearer $TOK\" -F file=@shopdbagent.exe -F version=1.6.3"
|
|
},
|
|
{
|
|
"path": "/api/endpoints/siteagent/binaries/<binkey>/detect",
|
|
"method": "POST",
|
|
"auth": "jwt + require_permission endpoints.agentpackage",
|
|
"params": "path: binkey; multipart/form-data: file",
|
|
"purpose": "Read the version out of an uploaded PE's resource table so the upload form can prefill it. Stores nothing",
|
|
"example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/agent/detect -H \"Authorization: Bearer $TOK\" -F file=@shopdbagent.exe"
|
|
},
|
|
{
|
|
"path": "/api/endpoints/siteagent/binaries/<binkey>/adopt",
|
|
"method": "POST",
|
|
"auth": "jwt + require_permission endpoints.agentpackage",
|
|
"params": "path: binkey; body: version",
|
|
"purpose": "Adopt a binary already staged on disk into the version manifest, for a build that predates the store",
|
|
"example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/agent/adopt -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"version\":\"1.6.2\"}'"
|
|
},
|
|
{
|
|
"path": "/api/endpoints/siteagent/binaries/<binkey>/current",
|
|
"method": "PUT",
|
|
"auth": "jwt + require_permission endpoints.agentpackage",
|
|
"params": "path: binkey; body: version (must already be staged)",
|
|
"purpose": "Point the current staged build at another version, so a package rolls back to it. The retention sweep never prunes the current one",
|
|
"example": "curl -X PUT http://localhost:5001/api/endpoints/siteagent/binaries/agent/current -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"version\":\"1.6.2\"}'"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"surface": "core-shopdbbackups",
|
|
"endpoints": [
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/shopdbbackups",
|
|
"auth": "admin",
|
|
"params": "none",
|
|
"purpose": "List every retained backup set, newest first, each with a compatibility flag saying whether it restores onto this build",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/shopdbbackups"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/shopdbbackups",
|
|
"auth": "admin",
|
|
"params": "none",
|
|
"purpose": "Kick off an on-demand backup run. Requires the backup overlay: without a configured backups root there is nothing behind it",
|
|
"example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/shopdbbackups"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/shopdbbackups/<snapshotid>",
|
|
"auth": "admin",
|
|
"params": "path: snapshotid",
|
|
"purpose": "Return one set's full manifest plus a resolved compatibility verdict",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/shopdbbackups/2026-09-06T120000Z"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/shopdbbackups/<snapshotid>/restore",
|
|
"auth": "admin",
|
|
"params": "path: snapshotid",
|
|
"purpose": "Queue a restore of a set, gated on schema compatibility. DESTRUCTIVE: the load drops and replaces every table",
|
|
"example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/shopdbbackups/2026-09-06T120000Z/restore"
|
|
},
|
|
{
|
|
"method": "POST",
|
|
"path": "/api/shopdbbackups/finalize-restore",
|
|
"auth": "admin",
|
|
"params": "none",
|
|
"purpose": "Queue the post-restore schema upgrade for the in-container finalizer, and retry it if a previous attempt failed. Does NOT run the upgrade in the request: that watcher runs it out of band, so a restore completes whether or not the browser that started it is still open, and Alembic cannot be killed by the worker timeout mid-chain",
|
|
"example": "curl -X POST -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/shopdbbackups/finalize-restore"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/shopdbbackups/status",
|
|
"auth": "admin",
|
|
"params": "none",
|
|
"purpose": "Live status of the most recent backup and restore runs, or idle when neither is moving",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/shopdbbackups/status"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/shopdbbackups/history",
|
|
"auth": "admin",
|
|
"params": "none",
|
|
"purpose": "Recorded backup and restore run history, newest first",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/shopdbbackups/history"
|
|
},
|
|
{
|
|
"method": "GET",
|
|
"path": "/api/shopdbbackups/settings",
|
|
"auth": "admin",
|
|
"params": "none",
|
|
"purpose": "Read the backup settings, with secret values masked",
|
|
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/shopdbbackups/settings"
|
|
},
|
|
{
|
|
"method": "PUT",
|
|
"path": "/api/shopdbbackups/settings",
|
|
"auth": "admin",
|
|
"params": "body: backup settings keys",
|
|
"purpose": "Update the backup settings. A masked secret submitted unchanged leaves the stored value alone",
|
|
"example": "curl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\"backup_schedule\":\"0 2 * * *\"}' http://localhost:5001/api/shopdbbackups/settings"
|
|
}
|
|
]
|
|
}
|
|
]
|