
## Manage governance workflow approvals

URL: https://docs.atlan.com/product/capabilities/build-apps/sdks/python/how-tos/manage-governance-workflow-approvals

> Discover, inspect, and bulk-approve or reject governance workflow tasks (the Inbox) programmatically using the Atlan Python SDK (pyatlan).

# Manage governance workflow approvals (Inbox)

Use `client.inbox` in the Atlan Python SDK to programmatically inspect and bulk-approve or bulk-reject governance workflow tasks—the items you see in Atlan's **Inbox**.

:::info[Requires the Governance Workflows and Inbox feature]
This system is enabled per tenant via **Admin → Labs → Governance Workflows and Inbox**. When it's on, new requests are handled by governance workflows and appear in the Inbox; the classic Requests module continues to hold any older requests. For the classic module, see [Manage requests](https://docs.atlan.com/llms/platform/python/manage-requests/llms.txt). A tenant can have items in both systems at the same time.
:::

## Understand three identifiers

Working with the Inbox involves three different GUIDs—knowing which one to use where saves a lot of confusion:

| Identifier | What it means | Where to use it |
|---|---|---|
| **Task GUID** | the Inbox item itself (a `Task` asset) | `group_key` in `approve_all` / `reject_all` to action **that one task** |
| **Related-asset GUID** | the asset the request targets | `group_key` to action **every pending task on that asset** in one call |
| **Workflow-request GUID** | the underlying approval record | `client.inbox.get()` only—the detail view with status and approver routing |

:::tip[Finding the workflow-request GUID]
The workflow-request GUID isn't a top-level task field—it's embedded in the task's `task_actions` fulfillment URL (`.../approval-workflow-requests/<guid>`). The discovery example below shows how to extract it. Passing a **task** GUID to `get()` fails—only the workflow-request GUID works there.
:::

## Discover pending tasks

Inbox tasks are `Task` assets, so you list them with a regular [search](https://docs.atlan.com/llms/platform/python/search-assets/llms.txt):

### Python

```python showLineNumbers title="List pending Inbox tasks"

from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import Task
from pyatlan.model.fluent_search import CompoundQuery, FluentSearch

client = AtlanClient()

search = (
 FluentSearch()
 .where(CompoundQuery.active_assets())
 .where(CompoundQuery.asset_type(Task)) # (1)
 .include_on_results(Task.TASK_RECIPIENT) # (2)
 .include_on_results(Task.TASK_EXECUTION_ACTION) # (3)
 .include_on_results(Task.TASK_RELATED_ASSET_GUID)
 .include_on_results(Task.TASK_ACTIONS)
).to_request()

for task in client.asset.search(search):
 if task.task_execution_action != "PENDING":
 continue
 wf_request_guid = None
 if task.task_actions: # (4)
 match = re.search(
 r"approval-workflow-requests/([0-9a-f-]{36})",
 str(task.task_actions[0].task_action_fulfillment_url),
 )
 wf_request_guid = match.group(1) if match else None
 print(task.guid, task.task_recipient, task.task_related_asset_guid, wf_request_guid)
```

1. Inbox items are assets of type `Task`.
2. The **recipient** is the user the task is waiting on—only this identity can action the task (see the warning below).
3. Execution state: `PENDING`, `APPROVED`, `REJECTED`, or `WITHDRAWN`.
4. Extracts the workflow-request GUID for use with `client.inbox.get()`.

## Inspect workflow request

To see a request's status and its approver routing:

### Python

```python showLineNumbers title="Get a workflow request"
request = client.inbox.get(guid=wf_request_guid) # (1)
print(request.status, request.approval_workflow_request_type)
print(request.approval_details) # (2)
```

1. Use the **workflow-request** GUID (not the task GUID).
2. `approval_details` shows the configured approvers and each one's decision—useful for understanding who a pending task is waiting on.

## Bulk-approve or reject tasks

Approvals and rejections are **group operations**: you name a group with `group_key`, and every pending task in that group (addressed to you) is actioned in a single server-side call.

### Python

```python showLineNumbers title="Approve one task, or all tasks on an asset"

# a task GUID actions exactly that one task

response = client.inbox.approve_all(
 group_key="24be8e31-4857-4f1a-b9e9-281db6c9f934", # (1)
 comment="Approved via automation",
)
print(response.total_tasks) # 1

# an asset GUID actions EVERY pending task on that asset

response = client.inbox.approve_all(
 group_key="48056772-a24d-4643-a880-affec0164e9f", # (2)
 sub_type="DATA_ACCESS", # (3)
 comment="Bulk approved",
)
print(response.total_tasks) # e.g. 2
```

1. `group_key` = task GUID → a group of one.
2. `group_key` = related-asset GUID → all pending tasks on that asset at once.
3. Optionally narrow by task sub-type: `DATA_ACCESS`, `CHANGE_MANAGEMENT`, `PUBLICATION_MANAGEMENT`, or `POLICY_APPROVAL` (see `ApprovalWorkflowRequestType`). Use `reject_all()` with the same arguments to reject.

### Raw REST API

```shell showLineNumbers title="Bulk-approve a task group"
curl -s -X PUT "https://tenant.atlan.com/api/service/approval-workflow-requests/actions/bulk" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"group_key": "48056772-a24d-4643-a880-affec0164e9f", "decision": "APPROVED", "comment": "Bulk approved"}'
```

:::info[Processing is asynchronous]
A successful call means the tasks were **queued** (`total_tasks` tells you how many). The state change lands a few seconds later—re-query the tasks and check `task_execution_action` if you need confirmation.
:::

## Who can action tasks

:::warning[Bulk actions are strictly recipient-scoped]
Only the user a task is **addressed to** (its `task_recipient`) can approve or reject it. An admin role doesn't override this. If none of a group's pending tasks are addressed to your token's identity, the API responds with error `1003` ("No pending tasks found for the specified group") even when the group visibly contains pending tasks for someone else. The same error also appears when every task in the group has already been actioned; check `task_execution_action` to tell the two cases apart.
:::

:::warning[Automation requires the approver to be your token's identity]
The governance workflow builder currently supports only **human users and groups** as approvers—an API key's or OAuth client's service account can't be named as an approver. That means Inbox approvals can only be automated with a token whose identity is a configured approver (for example, a user bearer token). Note that user session tokens are short-lived (~15 minutes), so they aren't a durable path for unattended automation. If you need headless Inbox approval, raise this with your Atlan contact—supporting service accounts as workflow approvers is a known product ask.
:::

## Good to know

- **Async client:** every method is also available on `AsyncAtlanClient`. Use `await client.inbox.approve_all(...)`.
- **A requester can't approve their own request** unless the workflow's approver configuration routes it back to them.
- **`get()` with an unknown GUID** currently returns a server error rather than `None`; treat errors from `get()` as "not a workflow-request GUID."

---
