Approval Workflows
Not every governance decision should be automated. Some actions — privilege escalation, bulk data operations, financial transactions — require a human to review and approve before they proceed. MITRITY's approval workflow system lets you define hold policies that pause agent actions and route them to a human approval queue.
This guide covers the end-to-end approval workflow: defining hold policies, managing the approval queue, configuring timeouts, and setting up notifications.
How Hold Policies Work
When the gateway evaluates an agent action and the matching policy has policy_type: "hold", the following sequence occurs:
- Action paused — The gateway holds the agent's request in memory and returns a
202 Acceptedresponse with a pending approval ID. - Approval created — The control plane creates an approval record with status
pendingand starts the timeout clock. - Notifications sent — If configured, MITRITY sends notifications to the designated channels (email, Slack, webhook).
- Human reviews — An authorized user reviews the pending action in the dashboard or via API.
- Decision applied — The user approves or denies the action. The gateway receives the decision and either forwards the original request or returns an error to the agent.
- Timeout fallback — If no human responds before the timeout expires, the
timeout_actiondetermines the outcome (auto-deny or auto-allow).
Agent ──action──► Gateway ──hold──► Control Plane ──notify──► Slack / Email
│ │
│◄────decision─────────┤◄────approve/deny──── Human
│ │
▼ ▼
forward or block log audit event
Defining a Hold Policy
Hold policies use the same structure as other policies, with additional fields for timeout configuration.
Policy Structure
{
"name": "hold-iam-changes",
"description": "Require human approval for IAM privilege changes",
"policy_type": "hold",
"action_pattern": "regex:^iam\\.(create_role|assign_role|escalate_permissions)$",
"priority": 400,
"enabled": true,
"hold_timeout_minutes": 15,
"timeout_action": "deny",
"constraints": {
"agent_id": ["agent-infra-bot", "agent-devops-bot"]
}
}
Hold-Specific Fields
| Field | Type | Required | Description |
|---|---|---|---|
hold_timeout_minutes | integer | Yes | Minutes to wait for a human decision before applying the timeout action. Minimum: 1, Maximum: 1440 (24 hours). |
timeout_action | enum | Yes | What happens when the timeout expires: "deny" (block the action) or "allow" (permit the action). |
Creating a Hold Policy via API
curl -X POST https://api.mitrity.com/api/v1/policies \
-H "Authorization: Bearer mk_live_your-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "hold-financial-transactions",
"description": "Hold all financial transactions above $1000 for manual approval",
"policy_type": "hold",
"action_pattern": "finance.transfer_*",
"priority": 350,
"hold_timeout_minutes": 30,
"timeout_action": "deny",
"constraints": {
"resource_pattern": "amount:>1000"
}
}'
Response:
{
"data": {
"id": "pol_8f3k2m1n",
"name": "hold-financial-transactions",
"description": "Hold all financial transactions above $1000 for manual approval",
"policy_type": "hold",
"action_pattern": "finance.transfer_*",
"priority": 350,
"hold_timeout_minutes": 30,
"timeout_action": "deny",
"enabled": true,
"constraints": {
"resource_pattern": "amount:>1000"
},
"created_at": "2026-03-01T10:00:00Z",
"updated_at": "2026-03-01T10:00:00Z"
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2026-03-01T10:00:00Z"
}
}
Common Hold Policy Examples
Privilege Escalation
Hold any action that modifies IAM roles or grants elevated permissions:
{
"name": "hold-privilege-escalation",
"policy_type": "hold",
"action_pattern": "regex:^(iam|rbac)\\.(create_role|assign_role|grant_permission|escalate)$",
"priority": 500,
"hold_timeout_minutes": 15,
"timeout_action": "deny"
}
Bulk Data Operations
Hold bulk export, delete, or migration operations:
{
"name": "hold-bulk-operations",
"policy_type": "hold",
"action_pattern": "regex:^bulk_(export|delete|migrate)_.*$",
"priority": 450,
"hold_timeout_minutes": 60,
"timeout_action": "deny"
}
Infrastructure Changes
Hold infrastructure modifications during business hours:
{
"name": "hold-infra-changes",
"policy_type": "hold",
"action_pattern": "infra.*",
"priority": 400,
"hold_timeout_minutes": 30,
"timeout_action": "deny",
"constraints": {
"time": {
"after": "08:00",
"before": "18:00",
"timezone": "Europe/Stockholm"
}
}
}
Database Schema Changes
Hold any DDL operation with a long timeout for DBA review:
{
"name": "hold-schema-changes",
"policy_type": "hold",
"action_pattern": "regex:^db\\.(alter_table|create_table|drop_table|create_index|drop_index)$",
"priority": 480,
"hold_timeout_minutes": 1440,
"timeout_action": "deny"
}
The Approval Queue
Dashboard View
The approval queue is accessible from the MITRITY dashboard at Governance > Approvals. The queue displays:
- Pending approvals sorted by creation time (oldest first)
- Agent name and ID that triggered the action
- Action type and parameters
- Policy name that matched
- Time remaining before timeout
Each pending approval shows the full context of the agent's action, including:
- The raw action payload
- The agent's declared mission scope
- Historical behavior patterns for this agent
- The current drift score
- Related recent audit events
Filtering the Queue
Filter pending approvals by:
| Filter | Description | Example |
|---|---|---|
| Agent | Show approvals for a specific agent | agent_id=agent-sales-bot |
| Policy | Show approvals triggered by a specific policy | policy_id=pol_8f3k2m1n |
| Action type | Filter by action pattern | action_type=iam.* |
| Time range | Show approvals created within a range | created_after=2026-03-01T00:00:00Z |
| Environment | Scope to a specific environment | environment_id=env_production |
Approve or Deny via Dashboard
To review a pending approval in the dashboard:
- Navigate to Governance > Approvals.
- Click on a pending approval to see the full context.
- Review the action details, agent mission scope, and drift score.
- Click Approve or Deny.
- Optionally add a reason (recommended for audit trail).
- The decision is applied immediately.
After a decision:
- Approved: The gateway forwards the original agent request. The audit event is logged with
decision: "allow"andapproval_status: "approved". - Denied: The gateway returns an error to the agent. The audit event is logged with
decision: "deny"andapproval_status: "denied".
Approve or Deny via API
List Pending Approvals
Retrieve all pending approvals for your tenant:
curl https://api.mitrity.com/api/v1/approvals?status=pending \
-H "Authorization: Bearer mk_live_your-api-key"
Response:
{
"data": [
{
"id": "apr_9x2k4m",
"status": "pending",
"agent_id": "agt_sales-bot",
"agent_name": "sales-bot",
"action_type": "bulk_export_contacts",
"action_params": {
"format": "csv",
"filters": { "region": "EMEA" },
"estimated_rows": 15420
},
"policy_id": "pol_8f3k2m1n",
"policy_name": "hold-bulk-operations",
"environment_id": "env_production",
"timeout_at": "2026-03-01T11:00:00Z",
"timeout_action": "deny",
"created_at": "2026-03-01T10:00:00Z"
}
],
"meta": {
"request_id": "req_def456",
"timestamp": "2026-03-01T10:05:00Z",
"next_cursor": null,
"total": 1
}
}
Approve an Action
curl -X POST https://api.mitrity.com/api/v1/approvals/apr_9x2k4m/approve \
-H "Authorization: Bearer mk_live_your-api-key" \
-H "Content-Type: application/json" \
-d '{
"reason": "Approved: EMEA contact export requested by sales director for quarterly review"
}'
Response:
{
"data": {
"id": "apr_9x2k4m",
"status": "approved",
"decided_by": "user_jsmith",
"reason": "Approved: EMEA contact export requested by sales director for quarterly review",
"decided_at": "2026-03-01T10:06:00Z"
},
"meta": {
"request_id": "req_ghi789",
"timestamp": "2026-03-01T10:06:00Z"
}
}
Deny an Action
curl -X POST https://api.mitrity.com/api/v1/approvals/apr_9x2k4m/deny \
-H "Authorization: Bearer mk_live_your-api-key" \
-H "Content-Type: application/json" \
-d '{
"reason": "Denied: No business justification provided for bulk export of 15K contacts"
}'
Response:
{
"data": {
"id": "apr_9x2k4m",
"status": "denied",
"decided_by": "user_jsmith",
"reason": "Denied: No business justification provided for bulk export of 15K contacts",
"decided_at": "2026-03-01T10:06:00Z"
},
"meta": {
"request_id": "req_jkl012",
"timestamp": "2026-03-01T10:06:00Z"
}
}
Count Pending Approvals
Get a quick count of pending approvals (useful for badge counters):
curl https://api.mitrity.com/api/v1/approvals/count?status=pending \
-H "Authorization: Bearer mk_live_your-api-key"
Response:
{
"data": {
"pending": 3,
"approved_today": 12,
"denied_today": 2,
"timed_out_today": 1
},
"meta": {
"request_id": "req_mno345",
"timestamp": "2026-03-01T10:10:00Z"
}
}
Timeout Behavior
When no human responds before the hold_timeout_minutes expires, the timeout_action determines the outcome.
Auto-Deny (Recommended)
{
"timeout_action": "deny"
}
When the timeout expires, the action is blocked. The gateway returns an error to the agent. The audit event is logged with decision: "deny" and approval_status: "timed_out".
Use auto-deny when:
- The action is high-risk (privilege escalation, data deletion, financial transactions)
- It is better to err on the side of caution
- The agent can retry the action later after explicit approval
Auto-Allow
{
"timeout_action": "allow"
}
When the timeout expires, the action is permitted. The gateway forwards the original request. The audit event is logged with decision: "allow" and approval_status: "timed_out".
Use auto-allow when:
- The action is low-to-medium risk and the hold is primarily for visibility
- Blocking the agent on timeout would cause unacceptable downstream failures
- You want human review when available, but default to permissive behavior
Timeout Recommendations
| Scenario | Recommended Timeout | Recommended Action |
|---|---|---|
| Privilege escalation | 15 minutes | deny |
| Bulk data export | 60 minutes | deny |
| Infrastructure changes | 30 minutes | deny |
| Schema changes (DBA review) | 24 hours (1440 min) | deny |
| Non-critical alerts | 30 minutes | allow |
| After-hours monitoring | 60 minutes | allow |
Notification Channels
MITRITY can notify designated channels when a new approval is pending. Notifications include the full context of the action and a direct link to the approval in the dashboard.
Email Notifications (SendGrid)
Email notifications are sent via SendGrid to designated team members. Configure email recipients in the dashboard at Settings > Notifications > Approval Emails.
Each email includes:
- Agent name and action type
- Policy that triggered the hold
- Action parameters (sanitized)
- Time remaining before timeout
- Direct link to approve or deny in the dashboard
Slack Notifications
Slack notifications are sent via incoming webhooks to designated channels. Configure the webhook URL in Settings > Notifications > Slack.
Example Slack notification:
:rotating_light: Approval Required
Agent: sales-bot
Action: bulk_export_contacts (15,420 rows)
Policy: hold-bulk-operations
Timeout: 60 minutes (auto-deny)
[View in Dashboard]
Webhook Notifications
For custom integrations, configure a webhook URL in Settings > Notifications > Webhooks. MITRITY sends a POST request with the approval details:
{
"event": "approval.pending",
"approval": {
"id": "apr_9x2k4m",
"agent_id": "agt_sales-bot",
"action_type": "bulk_export_contacts",
"policy_name": "hold-bulk-operations",
"timeout_at": "2026-03-01T11:00:00Z",
"timeout_action": "deny",
"dashboard_url": "https://mitrity.com/app/approvals/apr_9x2k4m"
},
"timestamp": "2026-03-01T10:00:00Z"
}
Approval Permissions
Not every user can approve or deny actions. Approval permissions follow the MITRITY RBAC model:
| Role | Can Approve | Can Deny | Can View Queue |
|---|---|---|---|
| Owner | Yes | Yes | Yes |
| Manager | Yes | Yes | Yes |
| Member | No | No | Yes |
| Viewer | No | No | Yes |
Managers can only approve actions within their access scope. If a Manager's scope is limited to the "Development" environment, they can only approve actions from agents in that environment.
Audit Trail
Every approval decision is recorded in the audit log with full context:
{
"id": "evt_abc123",
"agent_id": "agt_sales-bot",
"action_type": "bulk_export_contacts",
"decision": "allow",
"decision_source": "approval",
"approval": {
"id": "apr_9x2k4m",
"status": "approved",
"decided_by": "user_jsmith",
"reason": "Approved: EMEA contact export for quarterly review",
"wait_duration_seconds": 360,
"policy_id": "pol_8f3k2m1n"
},
"timestamp": "2026-03-01T10:06:00Z"
}
The audit trail captures:
- Who made the decision (user ID and email)
- When the decision was made
- The reason provided (if any)
- How long the action waited in the queue
- Whether the decision was manual or a timeout fallback
Best Practices
Start Narrow, Expand Gradually
Begin with hold policies on a small set of high-risk actions (IAM changes, bulk exports). As your team builds comfort with the approval workflow, expand to cover additional action types.
Set Reasonable Timeouts
Choose timeouts that balance security with operational needs. A 5-minute timeout on a database migration during off-hours will almost certainly auto-deny. Consider your team's response time when setting timeouts.
Always Provide Reasons
Encourage (or require) approvers to provide a reason with every decision. This creates a valuable audit trail and helps with post-incident analysis.
Use Environment Scoping
In multi-environment setups, consider different timeout and action configurations:
- Production: Strict holds, short timeouts, auto-deny
- Staging: Looser holds, longer timeouts, auto-allow
- Development: Minimal holds, or monitor mode instead
Monitor Timeout Rates
If a large percentage of approvals are timing out, your timeouts may be too aggressive or the wrong people are being notified. Review the timeout rate in the dashboard at Governance > Approvals > Analytics.
Related Documentation
- Writing Policies — Policy structure, pattern matching, and priority system
- Enforcement Modes — How Monitor, Alert, and Enforce modes interact with hold policies
- Policy Simulations — Test hold policies against historical data before deploying
- RBAC — Role-based access control and approval permissions
- API Overview — Full API reference for approval endpoints