Eviworx
Docs

Approvals API

Approvals brings together all approvals and confirmations in two parts: (1) EntityApproval — persisted multi-approver records with configurable strategies (ALL, ANY, MAJORITY, QUORUM), approval groups and configs, for changes, change templates, incidents (closure/data breach) and workflow approval steps; (2) the union inbox (GET /api/approvals/inbox), which merges pending decisions from several areas into one list (absences, asset handovers, change-task readiness and more).

Features
✓ One approval system for 4 entity types
✓ 4 strategies (ALL, ANY, MAJORITY, QUORUM)
✓ Reusable approval groups
✓ Approval configs per entity type
✓ Auto-approval (by field, permission or role)
✓ No self-approval by the requester
✓ Configurable rejection handling (failOnReject)
✓ Deadline for workflow approval steps (dueTimeAmount/dueTimeUnit)
✓ Automatic entity status sync
✓ Audit trail of every decision (+ activity log)

Supported Entity Types

Entity-Type Description Status Effect
CHANGE Change approval (multi-approver) → APPROVED / REJECTED
INCIDENT Incident closure and reopen (incl. GDPR data breach) → CLOSED / RESOLVED
CHANGE_TEMPLATE Change template approval → APPROVED / REJECTED
WORKFLOW Approval steps of running workflows (approver right: workflows.completeSteps) → step APPROVED / REJECTED

Approval steps in workflows can be decided automatically: auto-approve and auto-reject conditions check a field value of the workflow data, a permission or a role; multiple conditions are combined with AND or OR. Auto-reject is checked before auto-approve. A step gets its deadline via amount and unit (dueTimeAmount, dueTimeUnit); this yields the due date of the approval. A manual decision goes through the instance's approve endpoint (APPROVED or REJECTED, plus an optional comment); such a step cannot be completed instead. That way every decision takes the same path — same permission check, same trail, same rejection handling. The comment then appears in the running workflow under the step outputs. Configuration and examples: Workflows API →

Cross-Domain Pending Inbox (union inbox)

Beyond EntityApproval records, the union inbox (GET /api/approvals/inbox) collects pending decisions from several domains into one unified list. Each source returns only entries the caller may see; without the permission for a source it stays empty (no 403). Assigned WORK (tickets, incidents, problems, changes, change tasks, workflow steps) is separate and runs through /api/my-tasks.

ProviderSource
entityApprovalEntityApproval records (change/incident/template)
absenceAbsence requests (manager decision)
handoverAsset handover confirmations
changeTaskReadyChange-task "ready" confirmations
cascadingAwarenessCascading awareness notices

Endpoints Overview

Approvals (User-Facing)

Method Endpoint Description
GET/api/my-tasksAssigned work items (not decisions) — documented in the API overview
GET/api/my-tasks/countCount of assigned work items per scope (for badges)
GET/api/approvals/inboxUnion inbox of pending approvals across all sources (?types= CSV, ?limit 1–200 [default 50], ?offset)
GET/api/approvals/inbox/countCounts per source (badge/tabs)
POST/api/approvals/inbox/decideUniversal decision via the inbox ID (TYPE:id)
GET/api/approvals/checkChecks whether an entityType/subType requires approval (configuration data for create and close dialogs)
POST/api/approvals/:id/decideMake decision (approve/reject)

The union inbox has no permission of its own. Each source checks its own permissions, so end users see their confirmation tasks too. Anyone without permission for any source gets an empty list. Invalid input is rejected with 400: an unknown types value, and a non-numeric limit or one outside 1–200. Likewise /check requires a valid entityType.

The approvers and comments of an entity come from that entity's own detail payload (change, incident, problem) — the visibility rules of that entity apply there.

Approval Groups (Admin)

Method Endpoint Description
GET/api/admin/approval-groupsAll approval groups
POST/api/admin/approval-groupsCreate new approval group
PATCH/api/admin/approval-groups/:idUpdate group (partial)
DELETE/api/admin/approval-groups/:idDelete group
GET/api/admin/approval-groups/:id/membersGroup members
GET/api/admin/approval-groups/:id/eligible-membersEligible members (not yet in group) — ?search=, ?take/skip; returns {data, total}
POST/api/admin/approval-groups/:id/membersAdd member
PATCH/api/admin/approval-groups/:id/members/:memberIdUpdate member (e.g. required flag)
DELETE/api/admin/approval-groups/:id/members/:memberIdRemove member (memberId = entry ID)

Approval Configs (Admin)

Method Endpoint Description
GET/api/admin/approval-configsAll approval configurations
GET/api/admin/approval-configs/:idConfig details
POST/api/admin/approval-configsCreate new config
PUT/api/admin/approval-configs/:idUpdate config
DELETE/api/admin/approval-configs/:idDelete config

Configuration: Groups, Configs & Closure Policy

The configuration layer (approval groups + approval configs) is managed in the UI under Admin Center → Service Configuration → Approvals (/admin/approvals). The area has three tabs: Groups, Configurations and Closure Policy (= incident closure policy). All admin endpoints require a user login; API keys are not accepted.

Approval Group — Fields

FieldTypeDescription
nameString (1–50)Technical name, lowercase alphanumeric with hyphens
displayNameString (1–100)Display name
descriptionString? (max. 500)Description
typeenumCAB, EMERGENCY_CAB, INCIDENT_CLOSURE, INCIDENT_REOPEN, MAJOR_INCIDENT, DATA_PROTECTION, CUSTOM
defaultStrategyenumANY, ALL, QUORUM, MAJORITY
defaultQuorumInt?Minimum count for QUORUM
isActiveBooleanActive/inactive (PATCH only)
members[]userId + weight (1–10) + isBackup. eligible-members lists only users holding the required approver permission.

POST and PATCH validate the body strictly: a field not listed in this table is rejected with 400. name is set only on creation; members are managed via the members endpoints. On PATCH an omitted field stays unchanged; null clears description and defaultQuorum.

Approval Config (Closure Policy) — Fields

An approval config defines WHETHER and HOW an entity must be approved. For INCIDENT with a closure-related subType this is exactly the "Closure Policy". At most one config exists per (entityType, subType) (unique).

FieldTypeDescription
entityTypeenumCHANGE, INCIDENT, PROBLEM, CHANGE_TEMPLATE, WORKFLOW
subTypeString?e.g. CLOSURE, DATA_BREACH, EMERGENCY, MAJOR, P1, REOPEN … (refines the rule)
requiresApprovalBooleanDefault true. false → no approval needed (closure without sign-off)
approvalGroupIdcuid?Which group approves
strategy / quorumenum? / Int?Override; null = use group default
autoAssignBooleanDefault true — auto-assign approvers from the group
conditionsJSON?Condition matching, e.g. {"riskLevel":["HIGH","VERY_HIGH"]}
priorityInt (0–100)Higher = checked first (when multiple configs match)
isActiveBooleanActive/inactive

Incident Closure Policy: The Closure Policy tab configures for INCIDENT whether closing requires sign-off (requiresApproval), which group + strategy applies and optional conditions. Approvers need incidents.approveClosure; the DATA_BREACH subType requires incidents.acknowledgeDataBreach instead (GDPR). Multiple approvals on the same entity (e.g. closure + data breach) run in parallel.

Incident Reopen Approval: Reopening a closed incident requires approval by default (reopenRequiresApproval). It runs through a dedicated approval group of type INCIDENT_REOPEN resp. the subType REOPEN and through its own approver permission incidents.approveReopen. This keeps the reopen approval separate from the closure approval, so that "may close" and "may reopen" can be different groups of people. The reopen goes through POST /api/incidents/:id/reopen; after sign-off (decision via POST /:id/decide) the incident is set to ACKNOWLEDGED, on rejection it stays CLOSED. reopenOverride NEVER bypasses the approval requirement. Reopen & Lifecycle

Approval Strategies

Strategy Description Result
ALL All approvers must approve APPROVED when all approve, REJECTED on any rejection
ANY A single approver is sufficient APPROVED on first approval
MAJORITY Simple majority (>50%) APPROVED when >50% approve
QUORUM Configurable minimum count APPROVED when quorum count is reached

API Examples

Get My Pending Decisions (cross-domain)

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

Aggregated across all providers; each entry carries its source type and states whether it can be decided inline (decideMode: inline) or points to its own page (deeplink). Example (abbreviated):

{
  "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 } }
}

Decisions go through POST /api/approvals/inbox/decide with exactly that itemId ({ itemId, decision: "APPROVE" | "REJECT", reason?, comment? }) — the aggregator routes by prefix to the right provider. Items with rejectRequiresReason: true require a reason on REJECT.

Make Decision

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"
  }
}

When evaluation determines all required approvals are complete, the entity status is automatically updated (e.g., Change → APPROVED, Incident → CLOSED).

Check Whether Approval Is Required

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

This endpoint returns the configuration metadata for create/close dialogs. The live approval state of a specific entity is carried by its detail payload (e.g. GET /api/changes/:id).

Rejection Handling

For approval steps in workflows, the step configuration defines what happens after a rejection:

Setting Behavior
failOnReject (default: on)The workflow ends with status FAILED unless an escalation step is configured; the initiator is notified.
failOnReject offThe workflow continues with the next steps.
escalationStepIdApplies to every rejection — manual or automatic — and takes precedence over failOnReject: instead of failing, the specified step starts. Whoever handles that step is notified; if the system runs it, there is no notification, and if no handler can be determined it goes to the initiator.

Permissions

Permission Description
approvals.decideRecord decisions (on top of the assignment and the entity-specific approver right)
approvals.viewGroupsView approval groups (settings tab Groups)
approvals.manageGroupsCreate/update/delete approval groups
approvals.manageMembershipsManage group members (add/update/remove)
approvals.viewConfigsView approval configs + closure policy (settings tabs Configs/Closure Policy)
approvals.manageConfigsManage approval configs + closure policy

Who may decide: A decision (POST /:id/decide resp. /inbox/decide) requires three things: (1) the approvals.decide permission; (2) the decision is assigned to the caller (else 403 NOT_ASSIGNED); (3) the approver permission for the entity type — INCIDENT → incidents.approveClosure, INCIDENT:DATA_BREACH → incidents.acknowledgeDataBreach, INCIDENT:REOPEN → incidents.approveReopen, CHANGE/CHANGE_TEMPLATE → changes.approve, WORKFLOW → workflows.completeSteps. For critical actions permissions are checked against the current database state, so a just-revoked right takes effect immediately. The requester cannot approve their own request. The detail page and the union inbox apply identical checks.

Error Handling

Error HTTP Description
APPROVAL_NOT_FOUND404Approval ID does not exist
NOT_ASSIGNED403Approval is not assigned to the caller
ALREADY_DECIDED409Decision already made
REJECTION_COMMENT_REQUIRED400Rejection requires a comment
NO_VALID_APPROVERS400No valid approvers configured
GROUP_IN_USE409Group is used in active configs
Related Pages
Changes API →

Change approvals in detail

Incidents API →

Incident closure approval & GDPR