---
title: "Approvals"
---

# Approvals

The Neural Inverse Govern approvals inbox for agent actions, MCP tool calls, AI Gateway tool calls and just-in-time access, with who may decide, once and session grants, notification channels and SLAs.

When a rule says "ask a named approver", the action waits for a person in Govern. All of these requests meet in one approvals inbox, in the console service **Approvals** (pages Inbox, Channels, SLA and Decision history).

## What lands in the inbox

| Source | Comes from | Who may decide |
|---|---|---|
| `agent_policy` | An agent action whose rule is `ask` with `approval: approvers` | A named approver (or any holder of `agentpolicy:approve` when none is named) who also holds `approvals:approve` or is in the request's approver group |
| `mcp_call` | An MCP tool call that needs approval | Holders of `mcp:approve` |
| `access_activation` | A just-in-time activation of a binding that requires approval | Holders of `iam.activations:approve` where the role is granted |
| `agent_action` and other subject types | The approval service's own requests, including AI Gateway tool calls held by tool governance (action `ai_gateway.tool_call`) | Holders of `approvals:approve`, or members of the request's approver group |

Rules for every source:

- **The requester can never approve their own request** (`403 self_approval`).
- Decisions need a reason of 1 to 1000 characters.
- A holder of `approvals:read` sees every request; anyone else sees what they filed, voted on, decided or may decide.
- Every decision, and every refused attempt to decide, is audited as `approvals.inbox.decided`, besides the source's own audit event.

## Decide an agent action

An agent permission request shows the requester, project, engine, session, kind, the action, the rule that required approval and its approvers, and whether you may decide it.

| Decision | Effect |
|---|---|
| Approve once | The action may run once. The endpoint agent consumes the grant immediately before running it; it cannot be used twice. It expires after the organisation's `approval_ttl_seconds` (15 minutes by default). |
| Approve for the session | The same action in the same agent session is allowed until the grant expires (`session_grant_max_seconds`, 8 hours by default). |
| Deny | The action is refused. |

```bash
curl -sS -X POST "$KEYSTONE_URL/api/v1/orgs/$ORG_ID/agent-policy/approvals/$APPROVAL_ID/decision" \
  -H "Authorization: Bearer $APPROVER_TOKEN" -H 'Content-Type: application/json' \
  -d '{"decision": "approve", "scope": "once", "comment": "Release 4.2 tag"}'
```

While a request is pending, the developer's tool call is refused with a message naming it; the agent retries once the request is approved. A request nobody decides expires. Govern re-evaluates the action against the person's current policy every time it is asked, so a revocation or a changed rule takes effect before a stored request is answered.

Only the approver's own vote decides an agent request. If someone else voted on the shared request through the general approvals API first, the decision answers `409` and nothing is granted.

## The inbox API

| Route | Purpose |
|---|---|
| `GET /api/v1/approvals/inbox?view=open` (or `decided`) `&source=&cursor=&limit=` | One list across every source, with SLA state and `can_decide` for the caller |
| `GET /api/v1/approvals/inbox/{id}` | One request with its reason, parameters, votes and channel dispatches |
| `POST /api/v1/approvals/inbox/{id}/decision` | `{"decision": "approve" or "deny", "reason", "scope"}`; `scope` is required for items that take one (agent permissions: `once` or `session`) |
| `GET /api/v1/approvals/settings` | The SLA and the channels, without secrets |

Refusals: `403 not_an_approver` or `self_approval`; `409 out_of_turn`, `already_voted`, `already_decided` or `expired`; `503 source_unavailable` when a source does not answer. The routes need `api:access`; the source decides who may act.

## Notification channels

Approval requests can be sent to webhook, email, Slack and PagerDuty channels, and approved with signed single-use links. Configuring channels (`POST`, `DELETE /api/v1/approval-channels`) needs `approvals:admin`; reading them and dispatching a request (`POST /api/v1/approvals/{id}/dispatch`) need `api:access`. Channel URLs and credentials are never shown back; error messages are cut to the scheme and host.

A person who may decide a request can get a single-use token for it (`POST /api/v1/approvals/{id}/issue-token`), which `POST /api/v1/approvals/redeem` accepts once.

## SLA

`PUT /api/v1/approvals/sla` (`approvals:admin`) sets the account's thresholds: `warn_after_mins`, `escalate_after_mins`, `auto_reject_after_mins` and `escalation_target_group`, each 1 to 43200 minutes, in that order. The inbox shows each item's SLA state (`on_track`, `warning`, `escalation_due`, `escalated`, `overdue`, and once decided `met`, `late`, `auto_rejected` or `expired`).

SLA processing and recurring approvals are recorded as partial in the source. Do not rely on automatic escalation or auto-rejection without testing it in your deployment.

## Related

- Rules that create agent requests: [Agent policy](https://neuralinverse.com/docs/govern/agent-policy/).
- JIT activations: [Just-in-time access](https://neuralinverse.com/docs/govern/jit-access/).
- AI Gateway tool calls held for approval: [Guardrails and DLP](https://neuralinverse.com/docs/govern/guardrails/#governance-detectors).
