Changes API
Die Changes API verwaltet IT-Änderungen nach ITIL: Status-Maschine mit dedizierten Transition-Endpoints, Genehmigung über das Unified-Approval-Framework, strukturierte Change-Tasks (Implementation/Test/Rollback) mit individueller oder Gruppen-Zuweisung sowie Kalender-Einladungen (.ics) für geplante Arbeiten.
Endpoints Übersicht
| Method | Endpoint | Beschreibung |
|---|---|---|
GET | /api/changes | Liste mit Filtern ({data, pagination}; RBAC-gefiltert) |
GET | /api/changes/stats | Tab-Zähler (all / my / assigned) — akzeptiert dieselben Filter wie die Liste |
GET | /api/changes/:id | Einzelnes Change (per ID oder Nummer) |
POST | /api/changes | Neues Change erstellen (Status immer DRAFT) → 201 |
PATCH | /api/changes/:id | Change-Felder aktualisieren (KEIN Status, version Pflicht) |
DELETE | /api/changes/:id | Change löschen (Soft-Delete, kritische Aktion) |
POST | /api/changes/:id/restore | Gelöschtes Change wiederherstellen (Papierkorb-Liste: ?deleted=1) |
PATCH | /api/changes/:id/assign | Change Manager setzen/entfernen → {change, assignmentChanged} |
GET | /api/changes/:id/field-permissions | Editierbare/gesperrte/erforderliche Felder + erlaubte Transitions für aktuellen Status |
POST | /api/changes/:id/activity | Kommentar hinzufügen (Activity-Log selbst kommt über GET /:id) |
Enum-Werte in Großschreibung: Status, Typ, Priorität, Risiko, Impact und Dringlichkeit werden in Großschreibung gesendet und geliefert (NORMAL, VERY_HIGH, PENDING_APPROVAL …), auch in Filtern und Sortierung. Kleingeschriebene Werte ergeben 400.
Wer den Change sieht: Neben Antragsteller (viewOwn) und globaler Sicht (viewAll) öffnet auch die Beteiligung den Zugang: zugewiesener Change Manager, offene Genehmigung sowie, wer einen Change-Task trägt (direkt, über die zugewiesene Gruppe oder als Vertretung) und changes.viewOwn bzw. viewPendingApprovals hat. Das wirkt in Liste, Detail, Suche, Berichten, Anhängen und den Verknüpfungs-Karten. Bearbeitungs- und Task-Verwaltungsrechte bleiben davon unberührt. Ohne eines der Rechte viewAll, viewOwn oder viewPendingApprovals antworten Liste und Kennzahlen 403.
Transition-Endpoints (Status-Wechsel)
Wichtig: Status werden ausschließlich über diese dedizierten Endpoints gesetzt. PATCH /api/changes/:id akzeptiert KEIN status-Feld. Jeder Endpoint hat eine eigene Permission und Pflichtfelder im Body.
| Endpoint | Transition | Permission | Body |
|---|---|---|---|
POST /:id/submit | DRAFT → SUBMITTED | changes.submit | changeManagerId, notes? |
POST /:id/route-to-approval | SUBMITTED → PENDING_APPROVAL (oder APPROVED bei skipApproval) | changes.manageWorkflow + zugewiesener Change Manager | skipApproval?, notes? |
POST /:id/schedule | APPROVED → SCHEDULED | changes.schedule | scheduledStartTime, scheduledEndTime |
POST /:id/start-implementation | SCHEDULED → IN_PROGRESS | changes.startImplementation | notes? |
POST /:id/complete | IN_PROGRESS → COMPLETED | changes.markCompleted | resolution (≥20), closerId, actualEndTime? |
POST /:id/fail | IN_PROGRESS → FAILED | changes.markFailed | resolution (≥20), closerId, backoutPerformed? |
POST /:id/initiate-rollback | IN_PROGRESS → ROLLING_BACK | changes.backout | reason (≥20) |
POST /:id/backout | ROLLING_BACK → BACKED_OUT | changes.backout | resolution (≥20), closerId |
POST /:id/close | COMPLETED/FAILED/BACKED_OUT → CLOSED | changes.close | reviewNotes (≥20), successCriteriaMet? |
POST /:id/return-to-draft | * → DRAFT | changes.returnToDraft | reason (≥10) |
Fehler beim Routen zur Genehmigung: Ein STANDARD-Change braucht keine Genehmigungsrunde; der Versuch ergibt 400 CHANGE_APPROVAL_NOT_REQUIRED. Vorgesehen ist hier skipApproval: true. Findet sich kein verfügbarer Genehmiger, antwortet die Route mit 400 CHANGE_NO_APPROVERS_AVAILABLE.
Vollständiger Task-Satz als Voraussetzung: Die Lifecycle-Übergänge verlangen bis zum Abschluss den vollständigen Satz an Change-Tasks (je ein Task der Arten IMPLEMENTATION, TEST und ROLLBACK). Ein Change ohne Tasks lässt sich weder starten noch schließen — der Rückweg ist return-to-draft.
Approval-Endpoints
| Method | Endpoint | Beschreibung |
|---|---|---|
POST | /api/changes/:id/approvers | Approver zuweisen (legt einen Genehmigungseintrag an) |
POST | /api/approvals/:id/decide | Approver-Entscheidung (Unified Approval Framework) |
Entscheidungen (approve/reject) trifft der Genehmiger über die Approvals API. Siehe Approvals API.
Change-Tasks
Alle Routen liegen unter /api/changes/:changeId/tasks:
| Method | Endpoint | Beschreibung |
|---|---|---|
GET | / | Tasks des Changes auflisten |
POST | / | Task erstellen |
POST | /reorder | Reihenfolge ändern (pro kind, mit version) |
POST | /bulk-assign | Mehrere Tasks zuweisen |
POST | /bulk-skip | Mehrere Tasks überspringen |
POST | /bulk-delete | Mehrere Tasks löschen |
PATCH | /:taskId | Task aktualisieren (version Pflicht) |
DELETE | /:taskId?version=N | Task löschen (version als Query-Param) |
POST | /:taskId/assign | Person ODER Gruppe zuweisen |
POST | /:taskId/start | Task starten (→ IN_PROGRESS) |
POST | /:taskId/complete | Task abschließen (completionNote Pflicht) |
POST | /:taskId/skip | Task überspringen (skipReason Pflicht) |
POST | /:taskId/fail | Task als fehlgeschlagen markieren (failureReason Pflicht, ≥10 Zeichen) |
POST | /:taskId/retry | Fehlgeschlagenen Task erneut aufnehmen (nur aus FAILED, Begründung ≥10 Zeichen) |
POST | /:taskId/comments | Kommentar zum Task (≥10 Zeichen) → 201 |
Change Templates /api/changes/templates
Templates bilden wiederkehrende Standard-Changes (ITIL) ab: vordefinierte Standardwerte, gesperrte bzw. vom Anwender auszufüllende Felder und ein eigenes Task-Set. Templates sind versioniert und durchlaufen einen eigenen Genehmigungs-Workflow. Aus einem genehmigten Template werden Changes erzeugt — das Task-Set wird dabei aus dem genehmigten Versions-Snapshot geklont.
Template-Status
DRAFT → SUBMITTED(submit) → PENDING_APPROVAL → APPROVED → RETIRED(retire) Nebenwege:PENDING_APPROVAL → DRAFT (withdraw) APPROVED → DRAFT (new-version — neue Entwurfs-Version) DRAFT(verworfen) → APPROVED (discard-draft — zurück zur letzten genehmigten Version) Bearbeiten (PATCH, Tasks) ist nur im Status DRAFT möglich.Genehmigen und Ablehnen läuft über das Unified-Approval-Framework.
Endpoints
| Method | Endpoint | Beschreibung | Permission |
|---|---|---|---|
GET | /templates | Liste (Filter + offset/limit-Pagination) | changes.viewTemplates |
GET | /templates/approved | Nur genehmigte Templates (für Auswahl) | changes.viewTemplates |
GET | /templates/statistics | Template-Statistik | changes.viewTemplates |
GET | /templates/:id | Einzelnes Template | changes.viewTemplates |
GET | /templates/:id/versions | Versionshistorie | changes.viewTemplates |
GET | /templates/:id/versions/:version | Bestimmte Version | changes.viewTemplates |
GET | /templates/:id/changes | Changes, die dieses Template nutzen (limit/offset) | changes.viewTemplates |
GET | /templates/:id/activity | Activity-Historie (limit) | changes.viewTemplates |
GET | /templates/:id/tasks | Template-Tasks auflisten | changes.viewTemplates |
POST | /templates | Template erstellen (201, Status DRAFT) | changes.createTemplates |
PATCH | /templates/:id | Template aktualisieren (nur DRAFT) | changes.editOwnTemplates / editAllTemplates |
DELETE | /templates/:id | Template löschen (nur DRAFT, 204) | changes.deleteTemplates |
POST | /templates/:id/submit | DRAFT → PENDING_APPROVAL | changes.submitTemplates |
POST | /templates/:id/withdraw | PENDING_APPROVAL → DRAFT zurückziehen | changes.submitTemplates |
POST | /templates/:id/discard-draft | DRAFT verwerfen → letzte genehmigte Version | changes.editOwnTemplates / editAllTemplates |
POST | /templates/:id/retire | → RETIRED (Body reason?) | changes.retireTemplates |
POST | /templates/:id/new-version | APPROVED → neue DRAFT-Version (Body changeReason + Felder) | changes.editOwnTemplates / editAllTemplates |
POST | /templates/:id/create-change | Change aus Template erstellen (201) | changes.create |
POST | /templates/:id/tasks | Template-Task anlegen (nur DRAFT, 201) | changes.editOwnTemplates / editAllTemplates |
PATCH | /templates/:id/tasks/:taskId | Template-Task ändern (nur DRAFT) | changes.editOwnTemplates / editAllTemplates |
DELETE | /templates/:id/tasks/:taskId | Template-Task löschen (nur DRAFT, 204) | changes.editOwnTemplates / editAllTemplates |
POST | /templates/:id/tasks/reorder | Task-Reihenfolge ändern (nur DRAFT, 204) | changes.editOwnTemplates / editAllTemplates |
Schreib-/Aktions-Routen prüfen zusätzlich Ownership (Ersteller ODER editAllTemplates). Ein Template im Status DRAFT ist nur für seinen Ersteller sichtbar — es sei denn, er trägt changes.viewDraftTemplates ODER es existiert bereits eine genehmigte Version: dann zeigt die Vorlage ihre genehmigte Fassung jedem viewTemplates-Träger. Dieselbe Regel gilt für Liste, Detail und die Unterrouten (/versions, /changes, /activity, /tasks); ein Template außerhalb der eigenen Sicht liefert 404 TEMPLATE_NOT_FOUND, genau wie ein nicht existierendes. Genehmigung/Ablehnung läuft ausschließlich über Approvals API (
POST /api/approvals/:id/decide).
Template-Felder
| Feld | Typ | Beschreibung |
|---|---|---|
name | string | Slug, Pflicht (nur a–z, 0–9, Bindestrich) |
displayName | string | Anzeigename, Pflicht |
description | string | Beschreibung, Pflicht |
type | enum | STANDARD, NORMAL, EMERGENCY, MAJOR (Default STANDARD) |
categoryId | string? | Change-Kategorie |
defaultTitle / defaultDescription / defaultJustification | string? | Vorbelegte Inhalte des erzeugten Changes |
defaultRiskLevel / defaultImpact | enum | LOW, MEDIUM, HIGH, VERY_HIGH (Default LOW) |
defaultUrgency / defaultPriority | enum | LOW, MEDIUM, HIGH, CRITICAL (Default LOW) |
defaultRiskAssessment | any? | Vorbelegte Risikobewertung |
defaultAffectedServices / defaultAffectedAssets | string[] | Vorbelegte Betroffenheiten |
defaultPlannedDuration | number? | Vorbelegte geplante Dauer (Minuten) |
lockedFields | string[] | Beim Erstellen aus dem Template gesperrte Felder |
requiredUserFields | string[] | Felder, die der Anwender beim Erstellen ausfüllen muss |
Pagination der Template-Flächen: Die Template-Liste und die Liste der Changes eines Templates nutzen offset/limit-Pagination (Default 20, maximal 100), die Activity-Historie Default 50 / maximal 100 — anders als die Change-Liste (page/per). Die Enum-Werte sind hier wie überall in Großschreibung.
Template-Tasks
Template-Tasks definieren das Task-Set, das beim Erstellen eines Changes geklont wird. Verwaltbar nur, solange das Template im DRAFT ist.
| Feld | Werte | Beschreibung |
|---|---|---|
kind | IMPLEMENTATION, TEST, ROLLBACK | Art (nach Erstellung nicht änderbar — löschen + neu) |
phase | PREP, EXECUTE, VALIDATE, POSTCHECK | Phase (Default EXECUTE) |
title / description | string | Titel (Pflicht) / Beschreibung |
sortOrder | number | Reihenfolge |
estimatedMinutes | number? | Geschätzte Dauer |
requiredPermission | string? | Beim Ausführen geforderte Permission |
roleHint | string? | Hinweis auf zuständige Rolle |
suggestedGroupId | string? | Vorgeschlagene Agent-Gruppe |
dependsOnTemplateTaskIds | cuid[] | Vorgänger-Template-Tasks |
Change aus Template erstellen
POST /api/changes/templates/:id/create-change
{
"title": "Upgrade PostgreSQL on cluster-prod-2",
"scheduledStartTime": "2026-02-01T02:00:00Z",
"scheduledEndTime": "2026-02-01T04:00:00Z",
"assignedGroupId": "clx-dba-group-id"
}
Alle Body-Felder sind optional und überschreiben die Template-Defaults: title, description, justification, affectedServices, affectedAssets, scheduledStartTime, scheduledEndTime, plannedDuration, categoryId, assignedToId, assignedGroupId (per API-Key zusätzlich requestorId). Gesperrte Felder (lockedFields) bleiben unverändert; das Task-Set wird aus dem genehmigten Versions-Snapshot geklont.
Change-Kategorien
| Method | Endpoint | Beschreibung |
|---|---|---|
GET | /api/changes/categories | Alle Kategorien |
POST | /api/changes/categories | Kategorie erstellen |
PUT | /api/changes/categories/:id | Kategorie aktualisieren |
DELETE | /api/changes/categories/:id | Kategorie löschen |
Status-Maschine
Changes durchlaufen 12 Status. Jeder Übergang erfolgt über einen dedizierten Transition-Endpoint:
DRAFT → SUBMITTED → PENDING_APPROVAL → APPROVED → SCHEDULED → IN_PROGRESS → COMPLETED → CLOSED Alternative Pfade: PENDING_APPROVAL → REJECTED (Approver lehnt ab) * → DRAFT (via return-to-draft) IN_PROGRESS → FAILED (via fail) IN_PROGRESS → ROLLING_BACK → BACKED_OUT (via initiate-rollback + backout) COMPLETED/FAILED/BACKED_OUT → CLOSED (via close) Status-Beschreibung: • DRAFT = Entwurf, noch nicht eingereicht • SUBMITTED = Eingereicht, Change Manager gesetzt • PENDING_APPROVAL = Wartet auf Approvals (Unified-Approval-Framework) • APPROVED = Genehmigt • REJECTED = Abgelehnt • SCHEDULED = Für Wartungsfenster geplant • IN_PROGRESS = Umsetzung läuft (Change-Tasks werden abgearbeitet) • ROLLING_BACK = Rollback wird durchgeführt • COMPLETED = Erfolgreich abgeschlossen • FAILED = Fehlgeschlagen • BACKED_OUT = Zurückgerollt • CLOSED = Geschlossen & archiviert
Change-Typen
| Typ | Beschreibung | Approval-Anforderung |
|---|---|---|
| STANDARD | Routine-Änderungen, geringes Risiko (oft Template-basiert) | Häufig vorab genehmigt |
| NORMAL | Standard-Änderungen, mittleres Risiko | Regulärer Approval-Prozess |
| EMERGENCY | Notfall-Änderungen (z. B. dringende Sicherheitsupdates) | Fast-Track, Post-Implementation-Review |
| MAJOR | Große Änderungen, hohes Risiko | Erweiterte Approvals |
Change erstellen
Request
POST /api/changes
Antragsteller (requestorId): Bei einem angemeldeten Benutzer setzt der Server den Antragsteller auf den Aufrufer selbst; ein im Body mitgeschickter Wert wird ignoriert. Nur API-Key-Zugriffe müssen requestorId angeben. Der Status ist beim Anlegen immer DRAFT; alles Weitere läuft über die Transition-Endpoints.
{
"title": "Upgrade PostgreSQL to version 17",
"description": "Upgrade production database from PostgreSQL 16 to 17 for performance and security.",
"justification": "Security patches for CVE-2024-xxx. Performance improvements.",
"type": "NORMAL",
"categoryId": "clx-category-id",
"priority": "HIGH",
"riskLevel": "MEDIUM",
"impact": "HIGH",
"urgency": "MEDIUM",
"scheduledStartTime": "2026-02-01T02:00:00Z",
"scheduledEndTime": "2026-02-01T04:00:00Z",
"plannedDuration": 120,
"affectedServices": ["Database", "API"],
"affectedAssets": ["clx-asset-id"],
"riskAssessment": "Standard maintenance-window upgrade, rollback tested.",
"assignedToId": "clx-change-manager-id",
"tasks": [
{ "clientKey": "impl-1", "kind": "IMPLEMENTATION", "phase": "EXECUTE", "title": "Run pg_upgrade" },
{ "kind": "TEST", "phase": "VALIDATE", "title": "Run integration tests", "dependsOnTaskKeys": ["impl-1"] },
{ "kind": "ROLLBACK", "phase": "POSTCHECK", "title": "Restore from backup if needed" }
]
}
Response (201 Created)
{
"id": "clx...",
"number": "CHG-2026-000042",
"title": "Upgrade PostgreSQL to version 17",
"status": "DRAFT",
"type": "NORMAL",
"priority": "HIGH",
"riskLevel": "MEDIUM",
"impact": "HIGH",
"urgency": "MEDIUM",
"requestor": { "id": "clx...", "name": "John Doe", "email": "john@example.com" },
"assignedTo": { "id": "clx...", "name": "Change Manager" },
"createdAt": "2026-01-27T15:00:00.000Z"
}
Felder-Übersicht
| Feld | Typ | Pflicht? | Beschreibung |
|---|---|---|---|
title | string | ✓ | Kurztitel (5–200 Zeichen) |
description | string | ✓ | Detaillierte Beschreibung (20–5000 Zeichen) |
justification | string | ✓ | Begründung für den Change (20–2000 Zeichen) |
type | enum | ✓ | STANDARD, NORMAL, EMERGENCY, MAJOR |
categoryId | string | ✓ | Change-Kategorie (ID, Pflichtfeld) |
priority | enum | ✓ | LOW, MEDIUM, HIGH, URGENT, CRITICAL |
riskLevel | enum | ✓ | LOW, MEDIUM, HIGH, VERY_HIGH |
impact | enum | ✓ | LOW, MEDIUM, HIGH, VERY_HIGH |
urgency | enum | ✓ | LOW, MEDIUM, HIGH, CRITICAL |
requestorId | string | (API-Key) | Bei User-Auth = eingeloggter User; bei API-Key Pflicht |
tasks | array | Inline Change-Tasks (siehe Change-Tasks); nicht zusammen mit templateId | |
scheduledStartTime | DateTime | Geplanter Start | |
scheduledEndTime | DateTime | Geplantes Ende | |
plannedDuration | number | Geplante Dauer/Downtime (Minuten) | |
affectedServices | string[] | Betroffene Services | |
affectedAssets | string[] | Betroffene Assets | |
riskAssessment | string | Risikobewertung (Freitext) | |
assignedToId / assignedGroupId | string | Change Manager (Person oder Gruppe) | |
relatedTickets / relatedProblems | string[] | Verlinkte Tickets/Problems — verlangen das Verknüpfungs-Recht und die Sicht auf die Gegenseite, nur mit Benutzeranmeldung | |
templateId | string | Template-Referenz (Tasks werden aus Snapshot geklont) | |
fromProblemId | string | Cross-Entity-Erstellung aus einem Problem |
Verknüpfungen zu Tickets und Problems: relatedTickets und relatedProblems verlangen dasselbe Recht wie der Verknüpfungs-Endpunkt (changes.linkToTickets bzw. changes.linkToProblems, sonst 403), außerdem die Sicht auf die jeweilige Gegenseite. Ein Speichern, das die Verknüpfungen unverändert lässt, verlangt das Recht nicht. Sie sind an einen angemeldeten Benutzer gebunden (API-Key: 400 CHANGE_LINKS_REQUIRE_USER), und an einem geschlossenen Change lassen sich keine neuen Verknüpfungen anlegen. Jede Verknüpfung schreibt auf BEIDEN Seiten einen Timeline-Eintrag.
Genehmigungs-Workflow
Genehmigungen laufen über das zentrale Unified-Approval-Framework. Am Change werden Approver nur zugewiesen; die eigentliche Entscheidung erfolgt über die Approvals API.
Schritt 1: Einreichen (DRAFT → SUBMITTED)
POST /api/changes/:id/submit
{
"changeManagerId": "clx-change-manager-id",
"notes": "Ready for review"
}
Schritt 2: Approver zuweisen
POST /api/changes/:id/approvers
{
"userId": "clx-manager-id"
}
Response (201 Created)
{
"id": "clx-approval-id",
"userId": "clx-manager-id",
"required": true,
"decision": null,
"decisionAt": null,
"comment": null,
"user": { "id": "clx-manager-id", "name": "Jane Manager", "email": "jane@example.com" },
"sourceGroup": { "id": "clx-cab-group", "name": "cab", "displayName": "Change Advisory Board" },
"isManual": true,
"sequence": 0
}
- Berechtigung
changes.addApproverplus die Sicht auf diesen Change (dieselbe Prüfung wie bei Detail und Kommentar). - Nur möglich, solange der Change in SUBMITTED oder PENDING_APPROVAL ist (sonst
APPROVER_INVALID_STATUS). - 4-Augen-Prinzip: Requestor und zugewiesener Change Manager können nicht Approver sein (403
CONFLICT_OF_INTEREST). - Der manuell hinzugefügte Approver hängt an der laufenden Genehmigungsrunde — sourceGroup ist entsprechend gefüllt, und seine Entscheidung zählt in deren Auswertung mit.
Schritt 3: Zur Genehmigung routen (SUBMITTED → PENDING_APPROVAL)
POST /api/changes/:id/route-to-approval
{
"skipApproval": false,
"notes": "Routing to CAB"
}
Mit skipApproval: true springt der Change direkt auf APPROVED (Berechtigung changes.manageWorkflow).
Schritt 4: Approver entscheiden
POST /api/approvals/:id/decide
Die Entscheidung wird über das zentrale Approval-Framework getroffen. Sobald alle erforderlichen Approver zugestimmt haben, wechselt der Change automatisch auf APPROVED; bei Ablehnung auf REJECTED. Details, Request-/Response-Format und Permission changes.approve siehe Approvals API.
Change-Tasks im Detail
Change-Tasks bilden die konkrete Umsetzung ab. Jeder Task hat einen kind, eine phase, einen Status und ist entweder einer Person oder einer Agent-Gruppe zugewiesen.
| Feld | Werte | Beschreibung |
|---|---|---|
kind | IMPLEMENTATION, TEST, ROLLBACK | Art des Tasks |
phase | PREP, EXECUTE, VALIDATE, POSTCHECK | Phase (Default: EXECUTE) |
status | PENDING, BLOCKED, IN_PROGRESS, DONE, SKIPPED, FAILED | BLOCKED wenn Abhängigkeiten offen sind |
assignedToId XOR assignedGroupId | cuid | Person ODER Gruppe – niemals beides |
dependsOnTaskIds | cuid[] | Vorgänger-Tasks (Zyklus-Check serverseitig) |
estimatedMinutes / actualMinutes | number | Zeit-Tracking |
completionNote / skipReason / failureReason | string | Pflicht bei complete / skip / fail |
handoverNotes | string | Übergabe an abhängige Tasks |
version | number | Optimistic Lock – bei jeder Mutation Pflicht |
Task erstellen
POST /api/changes/:changeId/tasks
{
"kind": "IMPLEMENTATION",
"phase": "EXECUTE",
"title": "Run pg_upgrade on primary",
"description": "Execute pg_upgrade and verify cluster starts",
"assignedGroupId": "clx-dba-group-id",
"estimatedMinutes": 45,
"dependsOnTaskIds": ["clx-prep-task-id"]
}
Task-Lebenszyklus
# Assign user OR group (version required)
POST /api/changes/:changeId/tasks/:taskId/assign
{ "assignedToId": "clx-user-id", "version": 1 }
# Start – on group assignment the starting agent is atomically set as assignedToId
POST /api/changes/:changeId/tasks/:taskId/start
{ "version": 2 }
# Complete (completionNote required)
POST /api/changes/:changeId/tasks/:taskId/complete
{ "completionNote": "pg_upgrade completed, cluster healthy", "actualMinutes": 38, "version": 3 }
# Skip / fail
POST /api/changes/:changeId/tasks/:taskId/skip { "skipReason": "Not needed, already on v16", "version": 2 }
POST /api/changes/:changeId/tasks/:taskId/fail { "failureReason": "pg_upgrade aborted: incompatible cluster", "version": 2 }
Berechtigungen: Verwalten (create/update/delete/reorder/assign/skip/bulk/retry) erfordert changes.manageTasks ODER (changes.editOwn als Requestor) ODER Change-Assignee/aktives Gruppenmitglied. Ausführen (start/complete/fail) erfordert changes.manageTasks ODER (changes.executeTask mit Beteiligung). Kommentieren genügt eine der beiden Ebenen. Hinweis: changes.editAll allein gewährt KEINE Task-Rechte.
Die Task-Struktur ist während der Genehmigung eingefroren: Solange ein Change in PENDING_APPROVAL steht, lassen sich weder Tasks anlegen, ändern, löschen noch umsortieren — das Gremium bewertet einen unveränderlichen Vorschlag. Überspringen (skip) ist nur in der gerade aktiven Phase möglich. Ein fehlgeschlagener Task lässt sich über retry wieder aufnehmen.
Kalender-Einladungen (.ics)
Wird ein Change-Task einer Person zugewiesen und der Change hat scheduledStartTime und scheduledEndTime, verschickt Eviworx eine Kalender-Einladung (.ics, METHOD:REQUEST) an die zuständige Person. Bei Neuzuweisung/Storno geht eine CANCEL-Einladung an die vorherige Person.
- .ics-REQUEST nur, wenn der Change in einem der Status
SCHEDULED,APPROVED,IN_PROGRESSist und Start/Ende gesetzt sind. - CANCEL wird unabhängig vom Status verschickt, sobald Schedule-Daten existieren.
Change aktualisieren
PATCH /api/changes/:id
PATCH aktualisiert nur Datenfelder – KEIN status (dafür gibt es Transition-Endpoints). Erfordert changes.editAll ODER changes.editOwn (als Requestor). Eine Änderung von assignedToId/assignedGroupId zusätzlich changes.assign. Das Feld version ist PFLICHT (Optimistic Locking, 409 CHANGE_VERSION_CONFLICT bei veraltetem Stand); null leert ein Feld, ein weggelassener Schlüssel lässt es unverändert. Welche Felder im aktuellen Status editierbar/gesperrt/erforderlich sind, liefert GET /:id/field-permissions — ein gesperrtes Feld zu senden endet mit 400 LOCKED_FIELD_MODIFICATION.
Changes abrufen (Liste)
GET /api/changes?f.status=in:PENDING_APPROVAL,APPROVED&f.type=NORMAL&sort=scheduledStartTime:asc&page=1&per=20
Die Liste ist RBAC-gefiltert (viewAll / viewOwn / viewPendingApprovals) und unterstützt Filter und Sortierung. Ein Filter hat die Form f.<feld>=<operator>:<wert>; ohne Operator-Präfix gilt Gleichheit. Antwort:
{
"data": [ /* changes */ ],
"pagination": { "page": 1, "limit": 20, "total": 137, "totalPages": 7, "hasMore": true }
}
Query Parameters
| Parameter | Beschreibung |
|---|---|
f.status, f.type, f.priority, f.riskLevel, f.impact | Enum-Filter (eq/neq/in/notIn, Werte in Großschreibung) |
f.number, f.title | Text-Filter (eq/contains/startsWith) |
f.requestorId, f.assignedToId, f.categoryId | Zuordnungs-Filter (eq/in, teils isNull/isNotNull) |
f.scheduledStartTime, f.scheduledEndTime, f.createdAt, f.updatedAt | Zeit-Filter (gt/gte/lt/lte/between/relative) |
q | Suche über Nummer, Titel, Beschreibung |
page / per / sort | Seitenweise Ausgabe (per Standard 25, maximal 200) |
deleted=1 | Papierkorb: NUR gelöschte Changes (erfordert changes.viewDeleted; akzeptiert ausschließlich den Wert 1) |
includeDeleted=true | Mischliste inkl. gelöschter (erfordert changes.viewDeleted) |
relatedTicketId, relatedProblemId | Nach Verlinkung filtern |
Fehlerbehandlung
Self-Approval blockiert (4-Augen)
{
"errorCode": "CONFLICT_OF_INTEREST",
"message": "Requestor and assigned change manager cannot be an approver of this change."
}
Approver in falschem Status (400)
{
"errorCode": "APPROVER_INVALID_STATUS",
"message": "Cannot add an approver while the change is in status SCHEDULED",
"status": "SCHEDULED"
}
Pflichtfeld fehlt (Transition, 400)
{
"error": "VALIDATION_ERROR",
"details": [
{ "field": "resolution", "message": "Resolution notes must be at least 20 characters" }
]
}
Code-Beispiel: Kompletter Lifecycle (JavaScript)
// ===================================================
// COMPLETE CHANGE LIFECYCLE
// Status NEVER set via PATCH — always dedicated endpoints.
// ===================================================
const API_URL = 'https://your-instance.com/api';
const headers = { 'Content-Type': 'application/json' };
const post = (path, body) => fetch(`${API_URL}${path}`, {
method: 'POST', credentials: 'include', headers,
body: body ? JSON.stringify(body) : undefined,
}).then(r => r.json());
// 1. Create change (UPPER enum values, inline tasks)
const change = await post('/changes', {
title: 'Upgrade PostgreSQL to version 16',
description: 'Database upgrade for security and performance reasons',
justification: 'Security patches for CVE-2024-xxx, performance improvements',
type: 'NORMAL',
categoryId: 'clx-category-id',
priority: 'HIGH',
riskLevel: 'MEDIUM',
impact: 'HIGH',
urgency: 'MEDIUM',
scheduledStartTime: '2026-02-01T02:00:00Z',
scheduledEndTime: '2026-02-01T04:00:00Z',
plannedDuration: 120,
tasks: [
{ clientKey: 'impl', kind: 'IMPLEMENTATION', title: 'Run pg_upgrade' },
{ kind: 'TEST', title: 'Run integration tests', dependsOnTaskKeys: ['impl'] },
{ kind: 'ROLLBACK', title: 'Restore from backup if needed' },
],
});
console.log('Created:', change.number); // CHG-2026-000042
// 2. Submit (DRAFT → SUBMITTED), set change manager
await post(`/changes/${change.id}/submit`, { changeManagerId: 'clx-cm-id' });
// 3. Assign an approver (must not be requestor/change manager)
const approver = await post(`/changes/${change.id}/approvers`, { userId: 'clx-cab-member' });
// 4. Route to approval (SUBMITTED → PENDING_APPROVAL)
await post(`/changes/${change.id}/route-to-approval`, { skipApproval: false });
// 5. Approver decides via the unified approvals framework (different user!)
await post(`/approvals/${approver.id}/decide`, { decision: true, comment: 'Looks good' });
// → all required approvers done ⇒ change auto-transitions to APPROVED
// 6. Schedule (APPROVED → SCHEDULED) — .ics invites go out for assigned tasks
await post(`/changes/${change.id}/schedule`, {
scheduledStartTime: '2026-02-01T02:00:00Z',
scheduledEndTime: '2026-02-01T04:00:00Z',
});
// 7. Start implementation (SCHEDULED → IN_PROGRESS)
await post(`/changes/${change.id}/start-implementation`, { notes: 'Window opened' });
// 8. Work the tasks (start → complete, version-locked)
const tasks = await fetch(`${API_URL}/changes/${change.id}/tasks`, { credentials: 'include' }).then(r => r.json());
for (const t of tasks) {
await post(`/changes/${change.id}/tasks/${t.id}/start`, { version: t.version });
await post(`/changes/${change.id}/tasks/${t.id}/complete`, {
completionNote: 'Done and verified', version: t.version + 1,
});
}
// 9. Complete the change (IN_PROGRESS → COMPLETED)
await post(`/changes/${change.id}/complete`, {
resolution: 'Upgrade completed successfully, all tests green',
closerId: 'clx-cm-id',
});
// 10. Close (COMPLETED → CLOSED)
await post(`/changes/${change.id}/close`, {
reviewNotes: 'Post-implementation review passed, no incidents',
successCriteriaMet: true,
});
Rollback-Szenario
Wenn die Umsetzung schiefgeht, gibt es zwei Wege:
# A) Controlled rollback: IN_PROGRESS → ROLLING_BACK → BACKED_OUT
POST /api/changes/:id/initiate-rollback
{ "reason": "Integration tests failed, queries timing out — rolling back to v15" }
POST /api/changes/:id/backout
{ "resolution": "Restored from backup, cluster back on v15 and healthy", "closerId": "clx-cm-id" }
# B) Failure without rollback: IN_PROGRESS → FAILED
POST /api/changes/:id/fail
{ "resolution": "pg_upgrade aborted: incompatible cluster versions", "closerId": "clx-cm-id", "backoutPerformed": false }
# Then close: FAILED/BACKED_OUT → CLOSED
POST /api/changes/:id/close
{ "reviewNotes": "Root cause documented, retry planned for next window" }
- ✓ Status nur über Transition-Endpoints
- ✓ Genehmigung über Unified-Approval-Framework
- ✓ 4-Augen-Prinzip erzwungen
- ✓ Strukturierte Change-Tasks mit Optimistic Lock
- ✓ Kalender-Einladungen (.ics) für geplante Arbeit
changes.viewAll/viewOwn/viewPendingApprovalschanges.create/editAll/editOwnchanges.assign/addApproverchanges.submit/manageWorkflow/schedulechanges.startImplementation/markCompleted/markFailedchanges.backout/close/returnToDraftchanges.manageTasks/executeTaskchanges.delete/restorechanges.viewTemplates/viewDraftTemplates/createTemplateschanges.editOwnTemplates/editAllTemplates/submitTemplates/retireTemplates/deleteTemplates
Auth-/Rollenmodell: Permissions & RBAC
Problems API →
Erfahre mehr über die Problems API
Entity Linking API →
Changes mit Tickets/Problems/Incidents/Assets verknüpfen