Eviworx
Docs

Approvals API

Approvals bündelt alle Genehmigungen und Bestätigungen des Systems in zwei Teilen: (1) EntityApproval — persistierte Multi-Approver-Records mit konfigurierbaren Strategien (ALL, ANY, MAJORITY, QUORUM), Approval-Groups und -Configs, für Changes, Change-Templates, Incidents (Closure/Data-Breach) und Workflow-Genehmigungsschritte; (2) die Union-Inbox (GET /api/approvals/inbox), die offene Entscheidungen aus mehreren Bereichen in einer Liste zusammenführt (Abwesenheiten, Asset-Übergaben, Change-Task-Freigaben u. a.).

✅
Funktionen
✓ Ein Genehmigungssystem für 4 Entity-Types
✓ 4 Strategien (ALL, ANY, MAJORITY, QUORUM)
✓ Wiederverwendbare Approval-Groups
✓ Approval-Configs pro Entity-Type
✓ Auto-Approval (nach Feld, Recht oder Rolle)
✓ Kein Selbst-Genehmigen durch den Antragsteller
✓ Verhalten bei Ablehnung einstellbar (failOnReject)
✓ Frist für Workflow-Genehmigungsschritte (dueTimeAmount/dueTimeUnit)
✓ Automatischer Status-Sync der Entität
✓ Audit-Trail jeder Entscheidung (+ Activity-Log)

Unterstützte Entity-Types

Entity-Type Beschreibung Status-Auswirkung
CHANGE Change-Genehmigung (Multi-Approver) → APPROVED / REJECTED
INCIDENT Incident-Abschluss und -Wiedereröffnung (inkl. DSGVO Data-Breach) → CLOSED / RESOLVED
CHANGE_TEMPLATE Change-Template-Genehmigung → APPROVED / REJECTED
WORKFLOW Genehmigungs-Schritte laufender Workflows (Approver-Recht: workflows.completeSteps) → Schritt APPROVED / REJECTED

Genehmigungsschritte in Workflows können automatisch entschieden werden: Auto-Approve- und Auto-Reject-Bedingungen prüfen einen Feldwert der Workflow-Daten, ein Recht oder eine Rolle; mehrere Bedingungen werden mit UND oder ODER verknüpft. Auto-Reject wird vor Auto-Approve geprüft. Eine Frist erhält der Schritt über Menge und Einheit (dueTimeAmount, dueTimeUnit); daraus ergibt sich das Fälligkeitsdatum der Genehmigung. Von Hand entschieden wird ein solcher Schritt über den Approve-Endpoint der Instanz (APPROVED oder REJECTED, dazu ein optionaler Kommentar); abschließen lässt er sich nicht. So nimmt jede Entscheidung denselben Weg — mit derselben Rechteprüfung, demselben Protokoll und demselben Verhalten bei Ablehnung. Der Kommentar erscheint anschließend im Vorgang unter den Schritt-Ausgaben. Konfiguration und Beispiele: Workflows API →

Cross-Domain Pending-Inbox (Union-Inbox)

Über die EntityApproval-Records hinaus sammelt die Union-Inbox (GET /api/approvals/inbox) offene Entscheidungen aus mehreren Domains zu einer einheitlichen Liste. Jede Quelle liefert nur Einträge, die der Aufrufer sehen darf; fehlt das Recht für eine Quelle, bleibt sie leer (kein 403). Zugewiesene ARBEIT (Tickets, Incidents, Probleme, Changes, Change-Tasks, Workflow-Schritte) ist davon getrennt und läuft über /api/my-tasks.

ProviderQuelle
entityApprovalEntityApproval-Records (Change/Incident/Template)
absenceAbwesenheits-Anträge (Manager-Entscheidung)
handoverAsset-Handover-Bestätigungen
changeTaskReadyChange-Task „ready"-Bestätigungen
cascadingAwarenessCascading-Awareness-Hinweise

Endpoints Übersicht

Approvals (User-Facing)

Method Endpoint Beschreibung
GET/api/my-tasksZugewiesene Arbeits-Items (keine Entscheidungen) — dokumentiert in der API-Übersicht
GET/api/my-tasks/countAnzahl zugewiesener Arbeits-Items je Scope (für Badges)
GET/api/approvals/inboxUnion-Inbox der offenen Approvals über alle Quellen (?types= CSV, ?limit 1–200 [Default 50], ?offset)
GET/api/approvals/inbox/countZähler je Quelle (Badge/Tabs)
POST/api/approvals/inbox/decideUniversal-Entscheidung über die Inbox-ID (TYPE:id)
GET/api/approvals/checkPrüft, ob ein entityType/subType ein Approval erfordert (Konfigurationsdaten für Erstell- und Schließen-Dialoge)
POST/api/approvals/:id/decideEntscheidung treffen (Approve/Reject)

Die Union-Inbox hat keine eigene Berechtigung. Jede Quelle prüft ihre Rechte selbst, damit auch Endanwender ihre Bestätigungs-Aufgaben sehen. Wer für keine Quelle berechtigt ist, erhält eine leere Liste. Ungültige Eingaben lehnt die API mit 400 ab: ein unbekannter types-Wert sowie ein nicht-numerisches limit oder ein limit außerhalb 1–200. Ebenso verlangt /check einen gültigen entityType.

Approver und Kommentare einer Entität kommen aus deren eigenem Detail-Payload (Change, Incident, Problem) — dort gelten die Sichtrechte der jeweiligen Entität.

Approval Groups (Admin)

Method Endpoint Beschreibung
GET/api/admin/approval-groupsAlle Approval-Groups
POST/api/admin/approval-groupsNeue Approval-Group erstellen
PATCH/api/admin/approval-groups/:idGroup aktualisieren (partiell)
DELETE/api/admin/approval-groups/:idGroup löschen
GET/api/admin/approval-groups/:id/membersGroup-Mitglieder
GET/api/admin/approval-groups/:id/eligible-membersPotentielle Mitglieder (noch nicht in Group) — ?search=, ?take/skip; liefert {data, total}
POST/api/admin/approval-groups/:id/membersMitglied hinzufügen
PATCH/api/admin/approval-groups/:id/members/:memberIdMitglied aktualisieren (z.B. required-Flag)
DELETE/api/admin/approval-groups/:id/members/:memberIdMitglied entfernen (memberId = Entry-ID)

Approval Configs (Admin)

Method Endpoint Beschreibung
GET/api/admin/approval-configsAlle Approval-Konfigurationen
GET/api/admin/approval-configs/:idConfig Details
POST/api/admin/approval-configsNeue Config erstellen
PATCH/api/admin/approval-configs/:idConfig aktualisieren — Teiländerung; unbekannte ID 404
DELETE/api/admin/approval-configs/:idConfig löschen

Konfiguration: Groups, Configs & Closure Policy

Die Konfigurations-Ebene (Approval-Groups + Approval-Configs) wird in der UI unter Admin-Center → Service-Konfiguration → Genehmigungen verwaltet (/admin/approvals). Der Bereich hat drei Tabs: Groups, Configurations und Closure Policy (= Incident-Closure-Policy). Alle Admin-Endpoints erfordern eine Benutzer-Anmeldung; API-Keys werden nicht akzeptiert.

Approval-Group — Felder

FeldTypBeschreibung
nameString (1–50)Technischer Name, lowercase-alphanumerisch mit Bindestrichen
displayNameString (1–100)Anzeigename
descriptionString? (max. 500)Beschreibung
typeenumCAB, EMERGENCY_CAB, INCIDENT_CLOSURE, INCIDENT_REOPEN, MAJOR_INCIDENT, DATA_PROTECTION, CUSTOM
defaultStrategyenumANY, ALL, QUORUM, MAJORITY
defaultQuorumInt?Mindestzahl für QUORUM
isActiveBooleanAktiv/inaktiv (nur PATCH)
members[]—userId + weight (1–10) + isBackup. eligible-members listet nur User mit der nötigen Approver-Permission.

POST und PATCH prüfen den Body streng: Ein Feld, das diese Tabelle nicht nennt, lehnt die API mit 400 ab. name wird nur beim Anlegen gesetzt, Mitglieder laufen über die members-Endpoints. Beim PATCH bleibt ein weggelassenes Feld unverändert; null leert description und defaultQuorum.

Approval-Config (Closure Policy) — Felder

Eine Approval-Config legt fest, OB und WIE eine Entität genehmigt werden muss. Für INCIDENT mit subType-Bezug zur Schließung ist genau das die „Closure Policy". Pro (entityType, subType) existiert höchstens eine Config (unique).

FeldTypBeschreibung
entityTypeenumCHANGE, CHANGE_TEMPLATE, INCIDENT, WORKFLOW
subTypeString?z.B. CLOSURE, DATA_BREACH, EMERGENCY, MAJOR, P1, REOPEN … (verfeinert die Regel)
requiresApprovalBooleanDefault true. false → kein Approval nötig (Closure ohne Freigabe)
approvalGroupIdcuid?Welche Group genehmigt
strategy / quorumenum? / Int?Override; null = Group-Default verwenden
autoAssignBooleanDefault true — Approver automatisch aus der Group zuweisen
conditionsJSON?Bedingungs-Matching, z.B. {"riskLevel":["HIGH","VERY_HIGH"]}
priorityInt (0–100)Höher = zuerst geprüft (bei mehreren passenden Configs)
isActiveBooleanAktiv/inaktiv

Incident Closure Policy: Der Closure-Policy-Tab konfiguriert für INCIDENT, ob das Schließen eine Freigabe erfordert (requiresApproval), welche Group + Strategie greift und ggf. Bedingungen. Approver brauchen incidents.approveClosure; der SubType DATA_BREACH verlangt stattdessen incidents.acknowledgeDataBreach (DSGVO). Mehrere Approvals derselben Entität (z.B. Closure + Data-Breach) laufen parallel.

Incident Reopen Approval: Das Wiederöffnen eines geschlossenen Incidents ist standardmäßig genehmigungspflichtig (reopenRequiresApproval). Es läuft über eine eigene Approval-Gruppe vom Typ INCIDENT_REOPEN bzw. den subType REOPEN und über die eigene Approver-Berechtigung incidents.approveReopen. So bleibt die Reopen-Genehmigung vom Closure-Approval getrennt, und „darf schließen" und „darf wieder öffnen" können unterschiedliche Personenkreise sein. Der Reopen läuft über POST /api/incidents/:id/reopen; nach Freigabe (Entscheidung via POST /:id/decide) wird der Incident auf ACKNOWLEDGED gesetzt, bei Ablehnung bleibt er CLOSED. reopenOverride umgeht NIE die Approval-Pflicht. Reopen & Lifecycle

Approval-Strategien

Strategie Beschreibung Ergebnis
ALL Alle Approver müssen zustimmen APPROVED wenn alle zustimmen, REJECTED bei einer Ablehnung
ANY Ein einziger Approver reicht APPROVED bei erster Zustimmung
MAJORITY Einfache Mehrheit (>50%) APPROVED wenn >50% zustimmen
QUORUM Konfigurierbare Mindestzahl APPROVED wenn quorum-Anzahl zustimmt

API-Beispiele

Meine offenen Entscheidungen abrufen (cross-domain)

GET /api/approvals/inbox?types=CHANGE,HANDOVER_CONFIRM&limit=50&offset=0

Aggregiert über alle Provider; jeder Eintrag trägt seinen Quell-Typ und sagt, ob er direkt in der Liste entschieden werden kann (decideMode: inline) oder auf seine Seite verweist (deeplink). Beispiel (gekürzt):

{
  "data": [
    {
      "itemId": "CHANGE:approval-uuid-1",
      "type": "CHANGE",
      "entityId": "change-uuid",
      "entityNumber": "CHG-2026-000042",
      "title": "Upgrade PostgreSQL",
      "decideMode": "inline",
      "deeplinkUrl": "/changes/change-uuid",
      "inlineActions": { "rejectRequiresReason": true },
      "initiatedAt": "2026-03-17T10:00:00Z",
      "initiatedBy": { "id": "user-uuid", "name": "Jane Smith" },
      "priority": "HIGH"
    },
    {
      "itemId": "HANDOVER_CONFIRM:handover-uuid",
      "type": "HANDOVER_CONFIRM",
      "entityId": "handover-uuid",
      "entityNumber": "HO-00007",
      "title": "Dell XPS 15 Laptop",
      "decideMode": "deeplink",
      "deeplinkUrl": "/assets/handovers/handover-uuid",
      "initiatedAt": "2026-03-17T14:00:00Z"
    }
  ],
  "pagination": { "total": 2, "limit": 50, "offset": 0 },
  "counts": { "total": 2, "byType": { "CHANGE": 1, "HANDOVER_CONFIRM": 1 } }
}

Entschieden wird über POST /api/approvals/inbox/decide mit genau dieser itemId ({ itemId, decision: "APPROVE" | "REJECT", reason?, comment? }) — der Aggregator leitet anhand des Präfixes an den richtigen Provider weiter. Items mit rejectRequiresReason: true verlangen bei REJECT eine Begründung.

Entscheidung treffen

POST /api/approvals/:id/decide
{
  "decision": true,
  "comment": "Approved. Implementation plan looks solid."
}

Response

{
  "approval": {
    "id": "approval-uuid-1",
    "entityType": "CHANGE",
    "entityId": "change-uuid",
    "decision": true,
    "decisionAt": "2026-03-17T11:30:00Z",
    "comment": "Approved. Implementation plan looks solid."
  },
  "evaluation": {
    "isComplete": true,
    "outcome": "APPROVED",
    "approvals": 2,
    "rejections": 0,
    "pending": 0,
    "total": 2,
    "requiredForApproval": 2,
    "strategy": "ALL"
  }
}

Wenn die Evaluation ergibt, dass alle benötigten Approvals vorliegen, wird der Entity-Status automatisch aktualisiert (z.B. Change → APPROVED, Incident → CLOSED).

Genehmigungspflicht prüfen

GET /api/approvals/check?entityType=INCIDENT&subType=CLOSURE
{
  "requiresApproval": true,
  "strategy": "ALL",
  "approvalGroupId": "clx-group-incident-closure"
}

Der Endpoint liefert die Konfigurations-Metadaten für Create-/Close-Dialoge. Den laufenden Genehmigungsstand einer konkreten Entität trägt deren Detail-Payload (z.B. GET /api/changes/:id).

Verhalten bei Ablehnung

Bei Genehmigungsschritten in Workflows legt die Schritt-Konfiguration fest, was nach einer Ablehnung passiert:

Einstellung Verhalten
failOnReject (Standard: an)Der Workflow endet mit Status FAILED, sofern kein Eskalations-Schritt konfiguriert ist; der Initiator wird benachrichtigt.
failOnReject ausDer Workflow läuft mit den nächsten Schritten weiter.
escalationStepIdGreift bei jeder Ablehnung — von Hand wie automatisch — und geht failOnReject vor: statt zu scheitern, startet der angegebene Schritt. Benachrichtigt wird, wer diesen Schritt bearbeitet; führt ihn das System selbst aus, entfällt die Meldung, und lässt sich kein Bearbeiter bestimmen, geht sie an den Initiator.

Permissions

Permission Beschreibung
approvals.decideEntscheidungen buchen (zusätzlich zur Zuweisung und zum entity-spezifischen Approver-Recht)
approvals.viewGroupsApproval-Groups lesen (Settings-Tab Groups)
approvals.manageGroupsApproval-Groups anlegen/ändern/löschen
approvals.manageMembershipsGroup-Mitglieder verwalten (hinzufügen/ändern/entfernen)
approvals.viewConfigsApproval-Configs + Closure Policy lesen (Settings-Tabs Configs/Closure Policy)
approvals.manageConfigsApproval-Configs + Closure Policy verwalten

Wer entscheiden darf: Eine Entscheidung (POST /:id/decide bzw. /inbox/decide) setzt dreierlei voraus: (1) die Berechtigung approvals.decide; (2) die Entscheidung ist dem Aufrufer zugewiesen (sonst 403 ENTITY_APPROVAL_NOT_ASSIGNED); (3) die Approver-Berechtigung des Entitätstyps — INCIDENT → incidents.approveClosure, INCIDENT:DATA_BREACH → incidents.acknowledgeDataBreach, INCIDENT:REOPEN → incidents.approveReopen, CHANGE/CHANGE_TEMPLATE → changes.approve, WORKFLOW → workflows.completeSteps. Bei kritischen Aktionen werden die Rechte mit dem aktuellen Stand aus der Datenbank geprüft, damit ein gerade entzogenes Recht sofort wirkt. Wer die Genehmigung angefordert hat, kann sie nicht selbst erteilen. Detail-Seite und Union-Inbox prüfen identisch.

Fehlerbehandlung

Error HTTP Beschreibung
ENTITY_APPROVAL_NOT_FOUND404Approval-ID existiert nicht
APPROVAL_GROUP_NOT_FOUND · APPROVAL_GROUP_MEMBER_NOT_FOUND · APPROVAL_CONFIG_NOT_FOUND404Group, Mitglieds-Eintrag oder Config unbekannt (memberId = Entry-ID)
ENTITY_APPROVAL_NOT_ASSIGNED403Approval ist nicht dem Aufrufer zugewiesen
APPROVAL_CRITICAL_PERMISSION_REQUIRED403Dem Aufrufer fehlt die Approver-Berechtigung dieses Entitätstyps (details.permission nennt sie)
APPROVAL_MEMBER_MISSING_PERMISSIONS403Der Benutzer kann der Group nicht beitreten, ihm fehlen die geforderten Rechte (details.userName, details.missingPermissions)
ENTITY_APPROVAL_ALREADY_DECIDED409Entscheidung wurde bereits getroffen
APPROVAL_GROUP_NAME_EXISTS · APPROVAL_CONFIG_EXISTS409Der Group-Name ist vergeben (details.name) bzw. für dieses entityType/subType-Paar existiert schon eine Config
APPROVAL_GROUP_MEMBER_EXISTS · APPROVER_ALREADY_ASSIGNED409Der Benutzer ist bereits Mitglied der Group bzw. bereits Genehmiger dieser Entität
REJECTION_COMMENT_REQUIRED400Ablehnung erfordert einen Kommentar (details.field)
INVALID_ITEM_ID · UNSUPPORTED_APPROVAL_TYPE400Die itemId der Union-Inbox ist nicht in der Form TYPE:id lesbar, oder es gibt für diesen Typ keine Quelle
CASCADING_READONLY · CHANGE_TASK_READONLY400Der Eintrag ist ein Hinweis, kein Genehmigungs-Vorgang — erledigt wird er in der Detailansicht der Entität
HANDOVER_REQUIRES_DEEPLINK · CM_REVIEW_REQUIRES_DEEPLINK400Über die Union-Inbox nicht entscheidbar: Asset-Übergabe und Change-Manager-Prüfung verlangen die Pflichtangaben ihres eigenen Flusses
ABSENCE_NOT_PENDING400Der Abwesenheits-Antrag steht nicht mehr zur Entscheidung an
APPROVAL_GROUP_REQUIRED400Eine Config mit requiresApproval=true braucht eine Group und autoAssign=true — sonst wird niemand zugewiesen und der Abschluss bleibt blockiert
APPROVAL_GROUP_INACTIVE · APPROVAL_GROUP_NO_MEMBERS400Beim Zuweisen der Genehmiger: die Group ist inaktiv oder hat keine aktiven Mitglieder (details.groupName)
Verwandte Seiten
Changes API →

Change-Approvals im Detail

Incidents API →

Incident-Closure-Approval & DSGVO