> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zenable.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Approval Workflows

> Configure multi-phase approval flows for requirements and guardrails

<Info>
  Approval workflows are available on **Professional** and **Enterprise** plans.
</Info>

## Overview

Approval workflows let you gate requirements or guardrail changes. Configure multi-phase flows with flexible approval criteria based on specific users, roles, or permissions.

Without an active approval flow, requirements and guardrail regenerations are auto-approved immediately. With a flow configured, submissions enter a **pending** state and require explicit approval before proceeding.

## Key Concepts

| Concept                 | Description                                                            |
| ----------------------- | ---------------------------------------------------------------------- |
| **Approval Flow**       | A reusable workflow definition with one or more sequential phases      |
| **Phase**               | A step in the flow with its own approval expression (who must approve) |
| **Approval Expression** | A logical tree (AND/OR/NOT) of criteria that must be satisfied         |
| **Approval Request**    | A specific instance of a submission awaiting review                    |
| **Feedback**            | Votes (upvote/downvote) and comments on pending requests               |

## Target Types

Approval flows can be configured for these submission types. Create / update / delete are split per entity so different policies can apply to each (e.g. a flow could require approval on delete but not on create):

| Target Type                   | Description                                                                 |
| ----------------------------- | --------------------------------------------------------------------------- |
| `new_requirement`             | New requirement proposals                                                   |
| `requirement_version`         | Updates to existing requirements                                            |
| `requirement_delete`          | Deletion of a requirement family (cascades to the requirement's guardrails) |
| `requirement_override_create` | Creating a tenant override for a marketplace requirement                    |
| `requirement_override_update` | Modifying an existing tenant override                                       |
| `requirement_override_delete` | Removing a tenant override                                                  |
| `guardrail_regeneration`      | Guardrail code regeneration requests                                        |
| `guardrail_custom_create`     | Authoring a new tenant-owned custom guardrail                               |
| `guardrail_custom_update`     | Modifying an existing custom guardrail                                      |
| `guardrail_delete`            | Deleting a custom guardrail                                                 |
| `scope_create`                | Authoring a new scope definition                                            |
| `scope_update`                | Modifying an existing scope definition                                      |
| `scope_delete`                | Deleting a scope definition                                                 |
| `target_selector_create`      | Authoring a new approval-flow target selector                               |
| `target_selector_update`      | Modifying an existing target selector                                       |
| `target_selector_delete`      | Deleting a target selector                                                  |

### Pending state and final disposition

Approval-gated actions never reach their final state until the request is approved.

* **Creates and updates** stage the proposed data in the affected table itself, marked `PROPOSED` (overrides) or `PROPOSED` lifecycle (requirements). Read paths filter these out so users only see live state.
* **Deletes** carry no pending state on the row — the open approval request itself is the pending marker. On approve the row is soft-deleted via `deleted_at`; on reject the row is untouched. Soft delete keeps the audit trail intact and makes restoration trivial.
* An override **update** writes a new `PROPOSED` row whose `supersedes_id` points at the existing `ACTIVE` row. On approve, the superseded row is soft-deleted and the proposed row flips to `ACTIVE`. On reject, the proposed row is soft-deleted and the active row stays live.

### Specific-user criteria

`SPECIFIC_USER` criterion values are stored as `user_uid` (the stable 5-word identifier). The UI surfaces and accepts `login_identifier` (email / UPN / SSO sub) so authors and approvers see human-readable handles; the form picker stores the corresponding `user_uid` under the covers. Even when an approver holds `requirements:approve` or `guardrails:approve`, they can only decide on a request if they satisfy the active phase's expression — being named in a `SPECIFIC_USER` rule, for example.

## Creating an Approval Flow

Navigate to [Approval Management](https://www.zenable.app/approval-management?utm_source=docs\&utm_medium=web) and select the **Workflows** tab.

### Flow Configuration

1. **Name and description** - Give the flow a meaningful name
2. **Target types** - Select which submission types trigger this flow
3. **Phases** - Define one or more sequential approval phases
4. **Default flow** - Optionally mark as the default for its target types

### Approval Expressions

Each phase defines an approval expression using a logical tree:

* **Specific User** - A named user must approve (matched by user UID)
* **Role** - Any user with the specified role can approve
* **Permission** - Any user with the specified permission can approve

Combine criteria with **AND** (all must be satisfied), **OR** (any one suffices), or **NOT** (exclusion) operators. Expressions can be nested for complex approval requirements.

**Example:** "Requires approval from any Admin OR both the Security Auditor AND a Contributor"

```
OR
├── Role: Admin
└── AND
    ├── Role: Security Auditor
    └── Role: Contributor
```

### Multi-Phase Flows

Phases execute sequentially. Each phase must be fully approved before the next begins. If any phase is rejected, the entire request is rejected.

**Example:** A two-phase flow for new requirements:

1. **Technical Review** - Any contributor approves
2. **Compliance Sign-off** - Security auditor approves

## Reviewing Approval Requests

Navigate to the **Pending Reviews** tab in [Approval Management](https://www.zenable.app/approval-management?utm_source=docs\&utm_medium=web).

Each request shows:

* The submitted entity (requirement name, guardrail code snippet)
* Current phase and progress
* **Approve** / **Reject** buttons (visible only if you have the required permission)

### Decisions

* **Approve** - Records your approval for the current phase. If the phase's approval expression is now satisfied, the flow advances to the next phase (or completes).
* **Reject** - Immediately rejects the entire request regardless of phase. The submitter is notified.

### Feedback

You can provide feedback on any pending request without making an approval decision:

* **Vote** - Upvote or downvote (one vote per user per request, toggleable)
* **Comment** - Up to 280 characters. Edits are tracked; the last 12 versions are stored.

## What Happens After Approval

| Target Type                          | On Approval                                                                                   | On Rejection                        |
| ------------------------------------ | --------------------------------------------------------------------------------------------- | ----------------------------------- |
| New requirement                      | Transitions from PROPOSED to BETA; guardrail generation triggers                              | Transitions to DECLINED             |
| Requirement version                  | Transitions from PROPOSED to BETA; guardrail update triggers                                  | Transitions to DECLINED             |
| Requirement delete                   | Requirement soft-deleted; cascades to its guardrails                                          | Submitter notified; no delete       |
| Requirement override create / update | New row activates (PROPOSED → ACTIVE); for updates, the superseded ACTIVE row is soft-deleted | PROPOSED row soft-deleted           |
| Requirement override delete          | Override row soft-deleted                                                                     | Submitter notified; no delete       |
| Guardrail regeneration               | Regeneration proceeds with stored parameters                                                  | Submitter notified; no regeneration |
| Custom guardrail create              | New row becomes is\_latest=true                                                               | PROPOSED row soft-deleted           |
| Custom guardrail update              | New row becomes is\_latest=true; prior version flips to is\_latest=false                      | PROPOSED row soft-deleted           |
| Custom guardrail delete              | Guardrail soft-deleted                                                                        | Submitter notified; no delete       |
| Scope create / update                | New row activates (PROPOSED → ACTIVE); for updates, the superseded ACTIVE row is soft-deleted | PROPOSED row soft-deleted           |
| Scope delete                         | Scope soft-deleted                                                                            | Submitter notified; no delete       |
| Target selector create / update      | New row activates (PROPOSED → ACTIVE); for updates, the superseded ACTIVE row is soft-deleted | PROPOSED row soft-deleted           |
| Target selector delete               | Selector soft-deleted                                                                         | Submitter notified; no delete       |

## Notifications

Users receive notifications for approval events based on their [notification preferences](https://www.zenable.app/settings?utm_source=docs\&utm_medium=web):

| Event                   | Delivery     | Recipients                      |
| ----------------------- | ------------ | ------------------------------- |
| Approval requested      | Immediate    | Eligible approvers for phase 1  |
| Decision made           | Immediate    | Submitter                       |
| Phase completed         | Immediate    | Next phase's eligible approvers |
| Fully approved/rejected | Immediate    | Submitter                       |
| Feedback received       | Daily digest | Submitter                       |

All notification types can be individually enabled or disabled in user preferences.

## Required Permissions

Approvals are fully transparent: anyone with `approvals:read` can see every request, every decision, every comment, and who voted which way. There is no per-record permission gating beyond `approvals:read`.

| Permission             | Description                                                               |
| ---------------------- | ------------------------------------------------------------------------- |
| `approvals:read`       | View approval flows, pending requests, decisions, and the feedback thread |
| `approvals:feedback`   | Submit non-binding feedback (vote + comment) on an approval request       |
| `approvals:manage`     | Create, edit, and archive approval flow definitions                       |
| `requirements:approve` | Cast a binding approve/reject decision on requirement proposals           |
| `guardrails:approve`   | Cast a binding approve/reject decision on guardrail regeneration requests |

See [Roles and Permissions](/permissions) for which roles include these permissions.

## API Reference

### Approval Flows

| Method | Endpoint                        | Description                                                    | Permission         |
| ------ | ------------------------------- | -------------------------------------------------------------- | ------------------ |
| GET    | `/api/data/approval-flows`      | List flows (pass `?include_archived=true` to include archived) | `approvals:read`   |
| POST   | `/api/data/approval-flows`      | Create a new flow                                              | `approvals:manage` |
| PUT    | `/api/data/approval-flows/{id}` | Update a flow                                                  | `approvals:manage` |

### Approval Requests

| Method | Endpoint                                  | Description                                                                | Permission                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ------ | ----------------------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| GET    | `/api/data/approval-requests`             | List approval requests (filterable by status, target\_type, submitted\_by) | `approvals:read`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| GET    | `/api/data/approval-requests/{id}`        | Get request detail with phase instances                                    | `approvals:read`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| POST   | `/api/data/approval-requests/{id}/decide` | Submit approve/reject decision on a request                                | `requirements:approve` for requirement-related target types (`new_requirement`, `requirement_version`, `requirement_delete`, `requirement_override_create`, `requirement_override_update`, `requirement_override_delete`); `guardrails:approve` for guardrail-related target types (`guardrail_regeneration`, `guardrail_custom_create`, `guardrail_custom_update`, `guardrail_delete`); `approvals:manage` for scope/selector target types (`scope_create`, `scope_update`, `scope_delete`, `target_selector_create`, `target_selector_update`, `target_selector_delete`). The phase rule may also require a specific user / role / permission. |

Decision request body:

```json theme={null}
{
  "decision": "approve",
  "comment": "Looks good to me"
}
```

The `decision` field accepts `approve` or `reject`. The optional `comment` field (max 280 characters) is recorded with the decision.

### Approval Feedback

| Method | Endpoint                                    | Description                     | Permission           |
| ------ | ------------------------------------------- | ------------------------------- | -------------------- |
| GET    | `/api/data/approval-requests/{id}/feedback` | List all feedback for a request | `approvals:read`     |
| POST   | `/api/data/approval-requests/{id}/feedback` | Submit or update your feedback  | `approvals:feedback` |

Feedback request body:

```json theme={null}
{
  "vote": "upvote",
  "comment": "I agree with this change"
}
```

The `vote` field accepts `upvote` or `downvote` (mutually exclusive, toggleable). Both fields are optional; you can vote without commenting or comment without voting. Feedback is upserted per user per request — submitting again updates your existing feedback. Comment edits are versioned (last 12 versions retained).

## Audit Logging

All approval actions are recorded in the audit log:

* Flow created, updated, or archived
* Decision made (approve/reject)
* Request created or resolved
* Feedback submitted or edited

Access audit logs via the **Audit** section in the [management console](https://www.zenable.app/audit?utm_source=docs\&utm_medium=web).

<Note>
  **Need help?** Contact us at [hello@zenable.io](mailto:hello@zenable.io)
</Note>
