API-Endpoints
Agenten-Activities
| Methode | Route | Berechtigung |
|---|---|---|
| GET | /api/agents/runs?executionId=… | Leserecht am zugehörigen Workflow |
| GET | /api/agents/runs/{id}/events?after=0&pageSize=200 | Workflow-Leserecht; maximal 500/Seite |
| GET | /api/agents/mcp-servers | Admin/Operator; ohne Geheimnisse |
| GET | /api/agents/mcp-servers/{id}/tools | Admin/Operator; startet registrierte Verbindung |
| PUT, DELETE | /api/agents/mcp-servers/{id} | Admin |
| GET | /api/agents/skills | Admin/Operator |
| POST | /api/agents/skills | Admin; Version und Base64-ZIP, maximal 10 MB |
| PUT | /api/agents/skills/{id}/enabled | Admin; { "enabled": true } |
| DELETE | /api/agents/skills/{id} | Admin |
Ereignisse werden vor der Live-Meldung gespeichert und enthalten eine laufbezogene
Sequenz sowie optional eine Mitglieds-ID. Nach Reconnect über after nachladen. MCP-
Änderungen nehmen secrets nur schreibend entgegen und benötigen beim Bearbeiten das
aktuelle updatedAt. Skillversionen sind unveränderlich.
Allgemeines
Die REST-API verwaltet Workflows, Executions, Infrastruktur und Administration. In der lokalen Entwicklungsumgebung läuft sie auf Port 5000. Live-Status wird über SignalR unter /hubs/execution übertragen. Mutierende Workflow-Endpunkte liefern 423 Locked, wenn der aufrufende Benutzer nicht den Edit-Lock besitzt; disable ist davon ausgenommen.
JSON-Format: Property-Namen verwenden
camelCase. Enum-Werte werden als .NET-Name in PascalCase serialisiert, zum Beispiel"role":"Admin"und"status":"Succeeded". Die Anmeldung verwendet standardmäßig das httpOnly-Cookienp_auth.curlspeichert und sendet es mit-c cookie.jar -b cookie.jar. Die Beispiele verwenden$NP = "http://localhost:5000".
Die folgenden Bash-Beispiele setzen eine Anmeldung gemäß Authentifizierung und eine cookie.jar für diesen Server voraus. Diese Funktion vor den Beispielen definieren; sie liest den aktuellen CSRF-Wert auch nach einem Refresh:
NP=http://localhost:5000
csrf_token() {
awk '$6 == "np_csrf" {sub(/\r$/, "", $7); print $7}' cookie.jar
}
Workflows
| Endpoint | Zweck |
|---|---|
GET /api/workflows | Liste (Array, 500-Row-Cap, folder-RBAC-gefiltert). Zeilen tragen kein definitionJson — den Graphen liefert der Einzelabruf |
GET /api/workflows/paged | Seitenweise Liste für die UI: page, pageSize (1–200, Default 50), folderId, search (Name und Beschreibung), sortBy/sortDir (Default updated/desc), bis zu 10 ids; folder-RBAC-gefiltert; liefert {items, page, pageSize, total, totalPages}. Bewusst ohne np-Befehl und ohne MCP-Tool — np workflow list liefert dieselben Workflows |
GET /api/workflows/names | Nur Id und Name, nach Name sortiert, folder-RBAC-gefiltert. Für Flächen, die Workflow-Namen anbieten, ohne sonst etwas darzustellen — der Executions-Filter nutzt ihn statt der vollen Liste. Bewusst ohne np-Befehl und ohne MCP-Tool — np workflow list liefert dieselben Namen |
POST /api/workflows | Neu (Admin/Op) — 201 |
PUT /api/workflows/{id} | Update — 204 (423 ohne Lock, 409 bei Version-Konflikt) |
DELETE /api/workflows/{id} | Löschen (Admin) — 204 |
POST /{id}/execute | Startet Lauf — 202 + ExecutionId |
POST /{id}/duplicate | Duplizieren — 201 (Copy born disabled) |
POST /{id}/enable / disable | Kill-Switch — 204 |
POST /{id}/cancel-all | Cancelt alle Running- und Pending-Executions — 200 |
POST /{id}/lock / unlock | Edit-Lock — 200 |
POST /{id}/publish | Save + Enable + Unlock (atomar) — 200 |
POST /{id}/force-unlock | Admin-only, bricht fremden Lock — 200 |
PUT /{id}/concurrency-limit | Admin/Op. Body {"maxConcurrentExecutions": N} (1..1000, null = unbegrenzt, das Feld ist Pflicht) — 204. Kein Edit-Lock, kein Versionssprung |
# Liste + einzelner Workflow
curl -s -b cookie.jar "$NP/api/workflows" | jq '.[0] | {id,name,isEnabled,version,folderPath}'
curl -s -b cookie.jar "$NP/api/workflows/by-name/deploy-prod" | jq '{id,name,isEnabled}'
# Neu anlegen (definitionJson = JSON-Objekt als String, ≤5 MiB, Tiefe ≤64)
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/workflows" \
-H 'Content-Type: application/json' \
-d '{ "name": "Deploy App",
"description": "Deploys the web app",
"definitionJson": "{\"nodes\":[],\"edges\":[]}",
"folderId": null }'
# Update (erfordert Lock)
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X PUT "$NP/api/workflows/21f1c0d4-..." \
-H 'Content-Type: application/json' \
-d '{ "name": "Deploy App v2", "description": "updated",
"definitionJson": "{\"nodes\":[...],\"edges\":[...]}", "expectedVersion": 3 }' -i # 204
# Publish (atomar), Duplicate, Cancel-All
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/workflows/21f1c0d4-.../publish" \
-H 'Content-Type: application/json' \
-d '{ "name": "Deploy App", "description": null,
"definitionJson": "{\"nodes\":[...],\"edges\":[...]}", "expectedVersion": 4 }'
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/workflows/21f1c0d4-.../duplicate"
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/workflows/21f1c0d4-.../cancel-all" # {"total":3,"signalled":2}
409-Lock-Konflikt-Body: {"message":"Workflow is already locked by alice.","lockedByUserName":"alice","lockedAt":"..."}. 409-Version-Konflikt: {"code":"workflow_version_conflict","currentVersion":4}. PUT und publish verlangen expectedVersion, die Version, auf der der Entwurf basiert; weicht sie von der gespeicherten ab, antwortet der Server mit diesem 409 und überschreibt nichts.
Versionen & Contract
| Endpoint | Zweck |
|---|---|
GET /{id}/versions | Versions-History |
GET /{id}/versions/{v} | Spezifische Version |
POST /{id}/rollback/{v} | Rollback — Body {"reason": "..."} |
GET /{id}/contract | Input/Output-Contract |
GET /by-name/{name}/contract | By-name-Lookup (exact-case gewinnt, sonst case-insensitive; mehrdeutig → 409) |
curl -s -b cookie.jar "$NP/api/workflows/21f1c0d4-.../versions" | jq '.[0]'
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/workflows/21f1c0d4-.../rollback/12" \
-H 'Content-Type: application/json' -d '{"reason":"revert bad config"}'
# Contract: Inputs aus manualTrigger.parameters + Outputs aus returnData + System
curl -s -b cookie.jar "$NP/api/workflows/by-name/deploy-prod/contract" | jq
# { "workflowName":"Deploy App", "hasManualTrigger":true,
# "inputs":[{"name":"version","type":"string","required":true,...}],
# "outputs":[{"name":"__executionId","source":"system"},{"name":"deployResult","source":"single"}] }
Aufrufer ohne Edit-Recht auf den Ordner des Workflows sehen bei jedem Input mit Default statt des Werts "***". Die Maske betrifft nur die Anzeige: Ein nicht gesetzter Parameter bekommt beim Lauf weiterhin den echten Default.
Step-Test & Coverage
| Endpoint | Zweck |
|---|---|
POST /{id}/steps/{stepId}/test | Einzelner Step-Test |
GET .../test-context | Test-Kontext (?executionId=) |
GET .../test-context/runs | Verfügbare Runs |
GET /{id}/coverage?windowDays=N | Step-Coverage |
GET /{id}/step-health | Step-Health |
GET /{id}/step-stats?windowDays=N | Step-Statistiken |
# Step mit Mock-Variablen + Config-Override testen
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/workflows/21f1c0d4-.../steps/runHealth/test" \
-H 'Content-Type: application/json' \
-d '{ "mockVariables": { "checkDisk.output": "7", "checkDisk.param.freeGb": "7" },
"configOverride": { "script": "Get-Process", "timeoutSeconds": 30 } }' | jq
# { "success":true, "output":"COMPUTERNAME=SRV01",
# "outputParameters":{"computerName":"SRV01"}, "durationMs":412.3 }
curl -s -b cookie.jar "$NP/api/workflows/21f1c0d4-.../coverage?windowDays=7" | jq
Statt configOverride, das nur data.config des gespeicherten Nodes ersetzt, übergibt stepOverride den vollständigen aktuellen node.data-Stand (Activity-Typ, Config, Maschine, Credential, Output-Variable); weggelassene Ziel- oder Credential-Felder leeren die gespeicherten Werte. Beide Overrides schließen sich gegenseitig aus und verlangen Edit-Recht plus eine eigene aktive Edit-Sperre. Eine ausgewählte Maschine, die nicht mehr existiert, wird abgelehnt.
Import/Export
| Endpoint | Zweck |
|---|---|
GET /export | Bulk-Export |
GET /{id}/export | Einzel-Export |
POST /import?folderId={guid} | Import (Namenskollision → Suffix " (Imported 2)"; folderId optional, fehlt → Root, RBAC = Edit auf dem Ziel-Folder) |
POST /import-scorch?folderId={guid} | SCOrch-Import (.ois_export; gleiches Folder-Targeting) |
curl -s -b cookie.jar "$NP/api/workflows/21f1c0d4-.../export" -o deploy.envelope.json
import_result="$(curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/workflows/import" \
-H 'Content-Type: application/json' --data-binary @deploy.envelope.json)"
workflow_id="$(printf '%s' "$import_result" | jq -r '.workflows[0].id')"
# Import ist immer disabled; erst dieser ausdrückliche zweite Call schaltet den Workflow scharf.
curl -s -o /dev/null -w '%{http_code}\n' -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST \
"$NP/api/workflows/$workflow_id/enable"
Beide Importendpoints erzeugen Workflows immer deaktiviert und liefern deren IDs im Feld
workflows[].id. POST /api/workflows/{id}/enable ist idempotent, benötigt Admin/Operator plus
Edit auf dem Workflow-Folder und antwortet erfolgreich mit 204. Ein Edit-Lock oder eine unsichere
Workflowdefinition verhindert die Aktivierung weiterhin sichtbar. Envelope-Typ:
nodepilot-workflow-export/v1. Secrets werden hier redigiert (***) — Teilen-Artefakt, kein DR.
Executions
| Endpoint | Zweck |
|---|---|
GET /api/executions | Liste (?workflowId=&activeOnly=&terminalOnly=&includePayloads=, seitenweise: {items, page, pageSize, total, totalPages}). includePayloads ist als einziger dieser Parameter keine Filterung, sondern ändert die Antwortform: default true, mit false kommen returnData und inputParametersJson als null zurück. sortBy (started, status, workflow, trigger, startedBy, steps, duration) und sortDir (asc/desc, Default started/desc) sortieren serverseitig über alle Seiten; unbekannte Werte ergeben 400. Rein verengende Parameter (page, pageSize, status, search) stehen bewusst nicht in dieser Tabelle. |
GET /api/executions/{id} | Einzelne Execution |
GET /api/executions/{id}/steps | Steps einer Execution |
POST /api/executions/{id}/cancel / retry / resume | Einzelner Lauf |
GET /api/executions/{id}/paused-steps | Die Steps, auf denen ein Debug-Lauf pausiert, um den Debugger nach einem Neuladen der Seite wiederherzustellen (Leserecht auf den Workflow) |
curl -s -b cookie.jar "$NP/api/executions?workflowId=21f1c0d4-...&activeOnly=true" | jq '.items[0]'
curl -s -b cookie.jar "$NP/api/executions/7e3f..." | jq '{status,startedAt,completedAt,triggeredBy}'
curl -s -b cookie.jar "$NP/api/executions/7e3f.../steps" | jq '.[] | {stepId,status,durationMs}'
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/executions/7e3f.../cancel" -i # 204
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/executions/7e3f.../retry" -i # 202 + Location
resume-Body (Debug-Pause): {"stepId":"runHealth","mode":"continue|stepOver|stop","overrides":{"vars.targetHost":"srv02"}} → 204.
Machines & Credentials
| Endpoint | Zweck |
|---|---|
GET/POST/PUT/DELETE /api/machines | Maschinen (POST/PUT Admin/Op, DELETE Admin) |
GET /api/machines/options | Authentifizierte Maschinenauswahl: neun Konfigurations-/Verbindungsfelder, nach Name sortiert; keine Workflow- oder Activity-Statistiken |
POST /{id}/test | Maschinen-Verbindungstest — Body {"credentialId": null} |
GET/POST/PUT/DELETE /api/credentials | Credentials (DELETE Admin) |
# Maschine anlegen
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/machines" -H 'Content-Type: application/json' \
-d '{ "name":"SRV-PROD-01", "hostname":"srv-prod-01.contoso.com",
"winRmPort":5985, "useSsl":false,
"defaultCredentialId":"4c2a-...", "tags":"prod;web" }'
# Verbindungstest (credentialId null = Machine-Default)
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/machines/9f1a.../test" \
-H 'Content-Type: application/json' -d '{"credentialId":null}'
# { "success":true, "computerName":"SRV-PROD-01", "credentialUsed":"svc-winrm" }
# Credential (password min 8, wird nie zurückgegeben)
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/credentials" -H 'Content-Type: application/json' \
-d '{ "name":"svc-winrm", "username":"CONTOSO\\svc-winrm", "password":"p@ssw0rd!", "domain":null }'
# 201 → { "id":"...", "name":"svc-winrm", "username":"CONTOSO\\svc-winrm", "domain":null }
Global Variables
GET /api/global-variables (Admin/Op), POST/PUT/DELETE (Admin). Name-Pattern [A-Za-z0-9_-]{1,100}. Secrets werden gespeichert, aber nie zurückgegeben ("value":"***").
Ordner: GET /api/global-variable-folders (Admin/Op), POST, PUT/DELETE /{id}, POST /{id}/move (Admin). POST /api/global-variables/{id}/move-folder verschiebt eine Variable (Admin).
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/global-variables" -H 'Content-Type: application/json' \
-d '{ "name":"API_ENDPOINT", "value":"https://api.example.com",
"isSecret":false, "description":"Upstream API base URL" }'
# Secret anlegen — Response maskiert den Wert
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/global-variables" -H 'Content-Type: application/json' \
-d '{ "name":"SIGNING_KEY", "value":"-----BEGIN PRIVATE KEY-----...",
"isSecret":true, "description":null }'
# 201 → { ..., "value":"***", "isSecret":true }
Auth
| Endpoint | Zweck |
|---|---|
POST /api/auth/login / logout / refresh | Passwort-/LDAP-Login und Session-Lifecycle |
POST /api/auth/windows | Windows Negotiate/Kerberos |
GET /api/auth/oidc / oidc/callback | release-gated OIDC Authorization Code + PKCE |
GET /api/auth/me / methods | Profil / Auth-Methoden-Discovery |
/api/scim/v2/Users / Groups | release-gated SCIM-2.0-Provisionierung |
/api/scim/v2/ServiceProviderConfig / ResourceTypes / Schemas | SCIM-2.0-Discovery |
# Login — Token landet im httpOnly np_auth-Cookie (-c speichert es)
curl -s -c cookie.jar -X POST "$NP/api/auth/login" \
-H 'Content-Type: application/json' \
-d '{ "username":"admin", "password":"s3cret-pass" }'
# Ohne Header → { "userId":"...", "username":"admin", "role":"Admin" } (Token nur im Cookie)
# Aktuelle Identität und verfügbare Authentifizierungspfade
curl -s -b cookie.jar "$NP/api/auth/me" # {"id":"...","username":"admin","role":"Admin"}
curl -s "$NP/api/auth/methods" # {"local":true,"ldap":false,"windows":false,"windowsEndpoint":null,"oidc":false,"oidcEndpoint":null,"oidcDisplayName":null}
Details zu serverseitigen Sessions, AD SSO Preview, OIDC und Refresh: Authentifizierung & Rollen.
Audit
GET /api/audit (Admin, Cursor-Pagination), GET /api/audit/export?format=csv|ndjson.
# Filter + Cursor-Pagination (take max 500)
curl -s -b cookie.jar "$NP/api/audit?action=WORKFLOW_PUBLISHED&since=2026-06-01T00:00:00Z&take=50" | jq
# { "items":[...], "nextCursor":{"timestamp":"...","id":"..."} } → nextCursor als afterTs/afterId reichen
# Nächste Seite
curl -s -b cookie.jar "$NP/api/audit?afterTs=2026-06-25T10:00:00Z&afterId=7e3f...&take=50"
# Export (CSV Default; NDJSON-Stream ist ebenfalls camelCase-keys)
curl -s -b cookie.jar "$NP/api/audit/export?format=ndjson&since=2026-06-01T00:00:00Z" -o audit.ndjson
Audit-Codes folgen VERB_NOMEN. Vollständige Liste: Audit-Log.
Trigger & Webhooks
| Endpoint | Zweck |
|---|---|
POST /api/trigger/{workflowNameOrId} | External Trigger (X-Api-Key, Key-Scope auf Workflow-GUID und aktiver manualTrigger erforderlich) |
POST|GET|PUT|DELETE /api/webhooks/{workflow}/{path} | Webhook (Verb muss webhookTrigger.method matchen) |
# External Trigger — anonym, aber per Integrations-Key auf Workflow-GUIDs begrenzt. Optional Idempotency-Key (24h TTL, pro Key-Principal isoliert)
curl -s -X POST "$NP/api/trigger/nightly-reconcile" \
-H 'X-Api-Key: xyz' \
-H 'Idempotency-Key: ci-1719100000' \
-H 'Content-Type: application/json' \
-d '{ "parameters":{"day":"2026-06-25"}, "timeoutSeconds":null, "debug":false }'
# 202 → ExecutionResponse + Location; Replay → 200 + Idempotent-Replayed: true
# Webhook — X-Webhook-Secret wenn Node einen secret gesetzt hat
curl -s -X POST "$NP/api/webhooks/deploy-prod/github-push" \
-H 'X-Webhook-Secret: whsec-...' \
-H 'Content-Type: application/json' \
-d '{"ref":"refs/heads/main","after":"abcd123"}'
# 202 → {"workflowId":"...","executionId":"...","message":"Triggered"}
Alle Webhook-Reject-Pfade (missing/wrong secret, disabled, path/method-Mismatch, Maintenance-Window) liefern uniform 404 {"message":"Webhook endpoint not found"} — kein Leak, welche Bedingung scheiterte.
Observability & Diagnostics
| Endpoint | Zweck |
|---|---|
GET /api/observability/config|query|query_range|summary | Prometheus/OTel-Queries |
GET /api/observability/dashboards/{key} | Ein Dashboard aus dem festen Metrik-Katalog (alle Rollen) |
GET /api/diagnostics/support-log|support-log/download|support-events|support-events/export|app-log|app-log/download | Diagnostics (Admin) |
# config anonym; query/query_range/summary Admin/Op
curl -s "$NP/api/observability/config" | jq
curl -s -b cookie.jar "$NP/api/observability/query?query=up%7Bjob%3D%22nodepilot%22%7D" | jq
curl -s -b cookie.jar "$NP/api/observability/query_range?query=rate(nodepilot_workflows_total%5B5m%5D)&start=1719100000&end=1719103600&step=1m"
curl -s -b cookie.jar "$NP/api/observability/summary" | jq '.panels[] | {key,value}'
query ≤8 KiB, Metric-Name-Prefix-Allowlist, __name__-Selector rejected. 503 {"message":"Prometheus query endpoint is not configured."} falls nicht konfiguriert.
Backup, Users, Maintenance, Folders, Settings, DB-Admin
# Backup — multipart für preview/restore, Passphrase als Form-Field
curl -s -b cookie.jar "$NP/api/backup/manifest" | jq # {"sections":[{"section":"credentials","count":12},...]}
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/backup/export" -H 'Content-Type: application/json' \
-d '{ "sections":["credentials","globalVariables","workflows","settings"],
"passphrase":"correct-horse-battery-staple" }' -o backup.npbackup
# Restore: file + passphrase + policy (skip|rename|overwrite, per-section überschreibbar)
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/backup/restore" \
-F "file=@backup.npbackup" -F "passphrase=correct-horse-battery-staple" -F "policy=skip,Users=Overwrite"
# Users
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/users" -H 'Content-Type: application/json' \
-d '{ "username":"alice", "password":"p@ssw0rd!", "role":"Operator" }' # 201
# Settings — ETag-gated (If-Match required)
ETAG=$(curl -s -b cookie.jar -D - "$NP/api/admin/settings/Smtp" | tr -d '\r' | awk -F': ' '/^ETag:/ {print $2}')
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X PUT "$NP/api/admin/settings/Smtp" \
-H 'Content-Type: application/json' -H "If-Match: $ETAG" -d @smtp.json
# 428 ohne If-Match; 412 bei Mismatch; 400 boot-validation "would prevent booting"
# Im Cluster: PUT /api/admin/settings/Authentication → 409 CLUSTER_CONFIG_AS_CODE_REQUIRED
| Bereich | Endpoints |
|---|---|
| Backup | GET /api/backup/manifest, POST /export, POST /{preview|restore} (Admin, multipart) |
| Users | GET/POST /api/users, PUT/DELETE /api/users/{id}, POST /{id}/reactivate (Admin) |
| SCIM-Gruppen | GET /api/admin/scim-groups/tombstones, POST /{id}/reactivate (Admin) |
| Custom Activities | GET /api/custom-activities (alle Rollen sehen aktivierte Einträge; Admin/Op mit ?includeDisabled=true auch Entwürfe), GET /{id}, GET /{id}/versions, GET /export, POST, PUT/DELETE /{id}, POST /{id}/rollback/{version}, POST /import (Admin/Op; ein aktivierter Eintrag ist nur noch für Admins änderbar), POST /{id}/enable|disable (Admin) |
| Alerting-Regeln | GET /api/alerting/catalog|rules|rules/{id}|deliveries, POST /preview-filter|preview-rule (Admin/Op), POST /rules, PUT/DELETE /rules/{id}, POST /rules/{id}/enable|disable|test-fire (Admin) — siehe Alerting |
| System-Alert-Policies | GET /api/alerting/system/catalog|policies|policies/{id}, POST /preview (Admin/Op), POST /policies, PUT/DELETE /policies/{id}, POST /policies/{id}/enable|disable|test-fire (Admin) |
| Abhängigkeiten | GET /api/dependencies/objects (q, kind, state=problems|unused, sort=name|usage, cursor, limit), GET /{kind}/{id}, GET /{kind}/{id}/evidence — kind ist workflow|machine|credential|variable|customActivity; alle Rollen, beschränkt auf die lesbaren Ordner |
| Betrieb | GET /api/operations/graph?windowMinutes=30|60 — die Live-Betriebsansicht; alle Rollen, beschränkt auf die lesbaren Ordner |
| Maintenance Windows | GET /api/maintenance-windows, GET /{id}, GET /affecting/{workflowId}, POST, PUT/DELETE /{id} |
| Shared Folders | GET/POST /api/shared-workflow-folders, PUT/DELETE /{id}, POST /{id}/move, POST /api/workflows/{workflowId}/move-folder |
| Folder-Permissions | GET/POST /api/shared-workflow-folders/{folderId}/permissions, PUT/DELETE /{permissionId} |
| Settings | GET /api/admin/settings, GET|PUT /{section}, GET /status|system-info|effective-sizing, POST /test/smtp|test/llm|test/ldap, POST /restart-service (Admin; restart-service antwortet 202, oder 409 SERVICE_RESTART_UNAVAILABLE, wenn sich der Server nicht selbst neu starten kann; Authentication-PUT im Cluster = 409) |
| DB-Admin | GET /api/dbadmin/tables, GET|PATCH|DELETE /tables/{name}/rows, GET /info, POST /query (Admin) |
| Dashboard | GET /api/stats/dashboard, GET /api/stats/failure-causes?windowHours=N (1..720, Default 24 — letzte Fehlschläge, gruppiert nach normalisierter Meldung; folder-gescopt wie das Dashboard), GET /api/stats/sidebar-counts (drei Nav-Badge-Zähler für die SPA, folder-gescopt; bewusst ohne np-Befehl und ohne MCP-Tool — np stats dashboard liefert dieselben Zahlen), GET /api/stats/duration-trend?windowHours=N&workflowId= (1/24/168/720, Default 24, alles andere fällt auf 24 zurück — Median- und P95-Laufzeit je Bucket, für alle Workflows oder einen, folder-gescopt; ab 24 h aus vorberechneten Stunden-Histogrammen, auf wenige Prozent genau; Chart-Serie der SPA, bewusst ohne np-Befehl und ohne MCP-Tool — np stats dashboard liefert dieselben Läufe) |
| Activity-Catalog | GET /api/activity-catalog |
| Scheduler | GET /api/triggers/schedule/next-fires |
| System | GET /api/system/host-info (alle Rollen) |
| AI | POST /api/ai/generate-script|generate-workflow|generate-agent-team (Admin/Op), POST /api/ai/chat (all roles; applying changes Admin/Op), POST /api/ai/chat/applied + GET /api/ai/chat/activity/{workflowId} (Admin/Op, folder-RBAC), GET /api/ai/knowledge/capabilities + POST /api/ai/knowledge/ask (der globale AI-Chat, alle Rollen; die Quellen hängen von der Rolle ab) — opt-in, SSE streaming |
| Secrets | POST /api/secrets/reencrypt (Admin, kein Body) |
Shared-Folder-Permission-Grant-Body: {"principalType":"User","principalKey":"<guid>","role":"FolderEditor"} — Rollen FolderViewer|FolderOperator|FolderEditor|FolderAdmin, principalType User|Group (Group = AD-SID S-1-5-21-...).
Maintenance-Window-Create-Body:
{ "name":"Saturday Patch Reboot", "isEnabled":true, "mode":"Blackout",
"scopeKind":"Global", "recurrence":"OneTime",
"oneTimeStartUtc":"2026-06-27T22:00:00Z", "oneTimeEndUtc":"2026-06-28T06:00:00Z",
"weeklyDaysMask":0, "timeZoneId":"UTC", "targets":null }
Mode: Blackout|AllowOnly. ScopeKind: Global|Folders|Workflows (bei Folders/Workflows muss Targets nicht-leer sein). Recurrence: OneTime|Weekly|Cron — bei Cron sind cronExpression (Quartz-Syntax mit Sekundenfeld, z. B. 0 0 3 ? * SAT) und durationMinutes (1..10080) Pflicht; das Fenster ist bei jedem Fire für durationMinutes aktiv, interpretiert in timeZoneId.
AI (opt-in)
Llm:Enabled=false default. 503 {"code":"LLM_DISABLED",...} wenn deaktiviert, 503 {"code":"LLM_NO_ACTIVE_PROFILE",...} wenn aktiviert, aber kein LLM-Profil ausgewählt ist.
Skriptgenerierung liefert Server-Sent Events: delta.text-Fragmente ergeben zusammen das Skript; done enthält model und durationMs. Fehler vor Stream-Beginn sind normale HTTP-Fehler, spätere Fehler erscheinen als error-Event mit code und message. curl -N zeigt Fragmente ohne Ausgabepufferung.
# Script generieren (mit Upstream-Variablen-Kontext)
# Optionaler Editor-Kontext braucht zusätzlich includeCurrentScript:true; ohne das Flag ignoriert
# der Server ein mitgesendetes currentScript, weil es Passwörter oder Tokens enthalten kann.
curl -sN -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/ai/generate-script" -H 'Content-Type: application/json' \
-d '{ "prompt":"Write a PowerShell step that checks free disk space",
"workflowId":"21f1c0d4-...", "stepId":"runScript_1",
"upstreamVariables":[
{"stepId":"collectInfo","label":"Collect Info → $hostname",
"variable":"collectInfo.param.hostname","expression":"{{collectInfo.param.hostname}}","type":"string"}] }'
# 200 Content-Type: text/event-stream
# event: delta
# data: {"text":"Get-PSDrive ..."}
#
# event: done
# data: {"model":"gpt-4o","durationMs":1820}
# Workflow generieren
curl -s -b cookie.jar -H "X-CSRF-Token: $(csrf_token)" -X POST "$NP/api/ai/generate-workflow" -H 'Content-Type: application/json' \
-d '{ "prompt":"A workflow that checks disk space and emails on low" }'
# 200 → {"definitionJson":"{...}","suggestedName":"Check Disk Space","nodeCount":4,...}
Health
| Endpoint | Zweck |
|---|---|
GET /healthz/live | Liveness |
GET /healthz/ready | DB-Readiness; bei armed oder unavailable sofort 503, ohne Directory-Abhängigkeit |
GET /healthz/database | DB-Statusbericht für die UI: immer 200 mit {status: ok|armed|unavailable, sinceUtc, reason} |
GET /healthz/directory | separater LDAPS-/Service-Bind-Status |
GET /healthz/leader | HA-Leader-Probe (fail-closed) |
Bei offenem DB-Breaker werden /api, neue Hub-Transporte, der OIDC-Callback und geschützte Metrics
vor DB- oder Auth-Zugriffen beendet. Diese HTTP-Pfade verwenden 503, Retry-After und denselben Body:
{
"code": "DATABASE_UNAVAILABLE",
"message": "...",
"retryAfterSeconds": 15,
"reason": "Unreachable",
"retryable": true
}
reason ist Unknown, Unreachable, Wedged oder RejectedByServer; letzteres setzt
retryable: false. Ein einzelner Command-Timeout verwendet denselben Vertrag mit
code: DATABASE_TIMEOUT, fünf Sekunden Retry-Abstand und ohne reason. Auf einem bereits
etablierten Hub liefern Methoden dieselben Codes als SignalR-Fehler.
curl -s "$NP/healthz/live" ; echo
curl -s "$NP/healthz/ready" ; echo
curl -s "$NP/healthz/database"; echo
curl -s "$NP/healthz/directory"; echo
curl -s "$NP/healthz/leader"; echo # HA-Leader-Probe, Anonymous
Alle healthz-Endpoints sind AllowAnonymous.