# Approvals

> Human approval of actions requested by agents or blocked by environment protection.

Human approval of actions requested by agents or blocked by environment protection. Approving needs a passkey.

| Operation | Method and path |
| --- | --- |
| [List approval requests](#list-approval-requests) | `GET /v1/workspaces/{workspace}/approvals` |
| [Ask a human to approve a specific request](#ask-a-human-to-approve-a-specific-request) | `POST /v1/approvals` |
| [Get an approval request](#get-an-approval-request) | `GET /v1/approvals/{approval_id}` |
| [Approve a request (passkey required)](#approve-a-request-passkey-required) | `POST /v1/approvals/{approval_id}/approve` |
| [Deny a request](#deny-a-request) | `POST /v1/approvals/{approval_id}/deny` |
| [Withdraw a pending request (requester)](#withdraw-a-pending-request-requester) | `POST /v1/approvals/{approval_id}/cancel` |

## List approval requests

```http
GET /v1/workspaces/{workspace}/approvals
```

- **Operation:** `listApprovals`
- **Scopes:** `projects:read`
- **Paginated:** `?limit=` and `?cursor=`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `workspace` * | path | string | Workspace id (`ws_…`) or slug. |
| `status` | query | [ApprovalStatus](/docs/api/objects/approval-status/) |  |
| `project` | query | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | A page of Approval objects. | [ApprovalList](/docs/api/objects/approval-list/) |

**Errors:** `400`, `401`, `403`, `404`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/workspaces/acme/approvals \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "data": [
    {
      "id": "apr_01jbx2m9k7q4r8t5v6w3y0z1pr",
      "workspace_id": "string",
      "project_id": "string",
      "environment": "string",
      "status": "pending",
      "action": {
        "kind": "deployment.create",
        "summary": "Deploy web (commit a1b2c3d, \"Fix rota export\") to production",
        "method": "string",
        "path": "string"
      },
      "reason": "string",
      "why_required": [
        {
          "code": "environment_protected",
          "message": "production requires approval for changes made by agents",
          "policy": "guv.environment.protection"
        }
      ],
      "plan": {
        "plan_hash": "sha256:9f2c1e0b7a4d3c2b1a09f8e7d6c5b4a39281706f5e4d3c2b1a0f9e8d7c6b5a49",
        "dry_run": true,
        "summary": "1 to update. +£5.00/month at current usage.",
        "changes": [
          {
            "action": "update",
            "resource": {
              "type": "service",
              "id": "svc_01jbx2m9k7q4r8t5v6w3y0z1sv",
              "name": "web"
            },
            "environment": "production",
            "summary": "Raise web memory limit from 1 GB to 2 GB",
            "diff": [
              {
                "path": "/resources/memory_mb",
                "before": 1024,
                "after": 2048
              }
            ],
            "destructive": false,
            "requires_approval": true,
            "reason": "production requires approval for changes made by agents"
          }
        ],
        "cost": {
          "current_monthly": {
            "amount": "18.20",
            "currency": "GBP",
            "display": "£18.20"
          },
          "projected_monthly": {
            "amount": "23.20",
            "currency": "GBP",
            "display": "£23.20"
          },
          "delta_monthly": {
            "amount": "5.00",
            "currency": "GBP",
            "display": "£5.00"
          },
          "confidence": "medium"
        },
        "policy": {
          "decision": "approval_required",
          "reasons": [
            {
              "code": "environment_protected",
              "message": "production requires approval for changes made by agents",
              "policy": "guv.environment.protection"
            }
          ],
          "approver_roles": [
            "owner",
            "admin"
          ]
        },
        "requires_approval": true,
        "expires_at": "2026-10-23T15:00:00Z"
      },
      "risk": "low",
      "approver_roles": [
        "owner"
      ],
      "requested_by": {
        "type": "agent",
        "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
        "display": "Stewart Webb via agent claude-code-on-stewart-laptop",
        "via_agent": {
          "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
          "name": "claude-code-on-stewart-laptop"
        },
        "on_behalf_of": {
          "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
          "display": "Stewart Webb"
        }
      },
      "decided_by": {
        "type": "agent",
        "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
        "display": "Stewart Webb via agent claude-code-on-stewart-laptop",
        "via_agent": {
          "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
          "name": "claude-code-on-stewart-laptop"
        },
        "on_behalf_of": {
          "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
          "display": "Stewart Webb"
        }
      },
      "decided_at": "2026-10-22T13:05:00Z",
      "decision_note": "string",
      "operation_id": "string",
      "result": {
        "status": 1,
        "body": {}
      },
      "url": "https://guv.dev/",
      "expires_at": "2026-10-22T13:05:00Z",
      "created_at": "2026-10-22T13:05:00Z"
    }
  ],
  "next_cursor": "string",
  "has_more": false
}
```

## Ask a human to approve a specific request

```http
POST /v1/approvals
```

The `request_approval` MCP tool. Most approvals are created automatically when a call returns `approval_required`.

- **Operation:** `requestApproval`
- **Scopes:** `approvals:request`
- **Idempotency-Key:** required

**Request body** (`application/json`, required): [ApprovalCreate](/docs/api/objects/approval-create/)

| Field | Type | Description |
| --- | --- | --- |
| `request` * | object |  |
| `reason` | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `201` Created | Created. | [Approval](/docs/api/objects/approval/) |

**Errors:** `400`, `401`, `403`, `409`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/approvals \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "request": {
    "method": "POST",
    "path": "/v1/projects/primecarers/environments/production/services/web/deployments",
    "body": {}
  },
  "reason": "string"
}'
```

**Example response**

```json
{
  "id": "apr_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "workspace_id": "string",
  "project_id": "string",
  "environment": "string",
  "status": "pending",
  "action": {
    "kind": "deployment.create",
    "summary": "Deploy web (commit a1b2c3d, \"Fix rota export\") to production",
    "method": "string",
    "path": "string"
  },
  "reason": "string",
  "why_required": [
    {
      "code": "environment_protected",
      "message": "production requires approval for changes made by agents",
      "policy": "guv.environment.protection"
    }
  ],
  "plan": {
    "plan_hash": "sha256:9f2c1e0b7a4d3c2b1a09f8e7d6c5b4a39281706f5e4d3c2b1a0f9e8d7c6b5a49",
    "dry_run": true,
    "summary": "1 to update. +£5.00/month at current usage.",
    "changes": [
      {
        "action": "update",
        "resource": {
          "type": "service",
          "id": "svc_01jbx2m9k7q4r8t5v6w3y0z1sv",
          "name": "web"
        },
        "environment": "production",
        "summary": "Raise web memory limit from 1 GB to 2 GB",
        "diff": [
          {
            "path": "/resources/memory_mb",
            "before": 1024,
            "after": 2048
          }
        ],
        "destructive": false,
        "requires_approval": true,
        "reason": "production requires approval for changes made by agents"
      }
    ],
    "cost": {
      "current_monthly": {
        "amount": "18.20",
        "currency": "GBP",
        "display": "£18.20"
      },
      "projected_monthly": {
        "amount": "23.20",
        "currency": "GBP",
        "display": "£23.20"
      },
      "delta_monthly": {
        "amount": "5.00",
        "currency": "GBP",
        "display": "£5.00"
      },
      "confidence": "medium"
    },
    "policy": {
      "decision": "approval_required",
      "reasons": [
        {
          "code": "environment_protected",
          "message": "production requires approval for changes made by agents",
          "policy": "guv.environment.protection"
        }
      ],
      "approver_roles": [
        "owner",
        "admin"
      ]
    },
    "requires_approval": true,
    "expires_at": "2026-10-23T15:00:00Z"
  },
  "risk": "low",
  "approver_roles": [
    "owner"
  ],
  "requested_by": {
    "type": "agent",
    "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
    "display": "Stewart Webb via agent claude-code-on-stewart-laptop",
    "via_agent": {
      "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
      "name": "claude-code-on-stewart-laptop"
    },
    "on_behalf_of": {
      "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
      "display": "Stewart Webb"
    }
  },
  "decided_by": {
    "type": "agent",
    "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
    "display": "Stewart Webb via agent claude-code-on-stewart-laptop",
    "via_agent": {
      "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
      "name": "claude-code-on-stewart-laptop"
    },
    "on_behalf_of": {
      "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
      "display": "Stewart Webb"
    }
  },
  "decided_at": "2026-10-22T13:05:00Z",
  "decision_note": "string",
  "operation_id": "string",
  "result": {
    "status": 1,
    "body": {}
  },
  "url": "https://guv.dev/",
  "expires_at": "2026-10-22T13:05:00Z",
  "created_at": "2026-10-22T13:05:00Z"
}
```

## Get an approval request

```http
GET /v1/approvals/{approval_id}
```

Agents poll this (or the resulting operation) until the status is `executed`, `failed`, `denied` or `expired`. Supports `?wait=30` long-polling.

- **Operation:** `getApproval`
- **Scopes:** any signed-in principal
- **CLI:** `guv approvals wait`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `approval_id` * | path | string |  |
| `wait` | query | integer | Seconds to wait for a status change before responding. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | OK. | [Approval](/docs/api/objects/approval/) |

**Errors:** `400`, `401`, `403`, `404`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/approvals/app_01jbx2m9k7q4r8t5v6w3y0z1pr \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "id": "apr_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "workspace_id": "string",
  "project_id": "string",
  "environment": "string",
  "status": "pending",
  "action": {
    "kind": "deployment.create",
    "summary": "Deploy web (commit a1b2c3d, \"Fix rota export\") to production",
    "method": "string",
    "path": "string"
  },
  "reason": "string",
  "why_required": [
    {
      "code": "environment_protected",
      "message": "production requires approval for changes made by agents",
      "policy": "guv.environment.protection"
    }
  ],
  "plan": {
    "plan_hash": "sha256:9f2c1e0b7a4d3c2b1a09f8e7d6c5b4a39281706f5e4d3c2b1a0f9e8d7c6b5a49",
    "dry_run": true,
    "summary": "1 to update. +£5.00/month at current usage.",
    "changes": [
      {
        "action": "update",
        "resource": {
          "type": "service",
          "id": "svc_01jbx2m9k7q4r8t5v6w3y0z1sv",
          "name": "web"
        },
        "environment": "production",
        "summary": "Raise web memory limit from 1 GB to 2 GB",
        "diff": [
          {
            "path": "/resources/memory_mb",
            "before": 1024,
            "after": 2048
          }
        ],
        "destructive": false,
        "requires_approval": true,
        "reason": "production requires approval for changes made by agents"
      }
    ],
    "cost": {
      "current_monthly": {
        "amount": "18.20",
        "currency": "GBP",
        "display": "£18.20"
      },
      "projected_monthly": {
        "amount": "23.20",
        "currency": "GBP",
        "display": "£23.20"
      },
      "delta_monthly": {
        "amount": "5.00",
        "currency": "GBP",
        "display": "£5.00"
      },
      "confidence": "medium"
    },
    "policy": {
      "decision": "approval_required",
      "reasons": [
        {
          "code": "environment_protected",
          "message": "production requires approval for changes made by agents",
          "policy": "guv.environment.protection"
        }
      ],
      "approver_roles": [
        "owner",
        "admin"
      ]
    },
    "requires_approval": true,
    "expires_at": "2026-10-23T15:00:00Z"
  },
  "risk": "low",
  "approver_roles": [
    "owner"
  ],
  "requested_by": {
    "type": "agent",
    "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
    "display": "Stewart Webb via agent claude-code-on-stewart-laptop",
    "via_agent": {
      "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
      "name": "claude-code-on-stewart-laptop"
    },
    "on_behalf_of": {
      "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
      "display": "Stewart Webb"
    }
  },
  "decided_by": {
    "type": "agent",
    "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
    "display": "Stewart Webb via agent claude-code-on-stewart-laptop",
    "via_agent": {
      "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
      "name": "claude-code-on-stewart-laptop"
    },
    "on_behalf_of": {
      "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
      "display": "Stewart Webb"
    }
  },
  "decided_at": "2026-10-22T13:05:00Z",
  "decision_note": "string",
  "operation_id": "string",
  "result": {
    "status": 1,
    "body": {}
  },
  "url": "https://guv.dev/",
  "expires_at": "2026-10-22T13:05:00Z",
  "created_at": "2026-10-22T13:05:00Z"
}
```

## Approve a request (passkey required)

```http
POST /v1/approvals/{approval_id}/approve
```

Needs a step-up passkey assertion with purpose `approval` bound to this approval. The platform then runs the stored request as the requester and returns the updated approval.

- **Operation:** `approveApproval`
- **Scopes:** `approvals:decide`
- **CLI:** `guv approve`
- **Idempotency-Key:** required

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `approval_id` * | path | string |  |

**Request body** (`application/json`, required): [ApprovalDecision](/docs/api/objects/approval-decision/)

| Field | Type | Description |
| --- | --- | --- |
| `assertion` * | [PasskeyAssertion](/docs/api/objects/passkey-assertion/) |  |
| `note` | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | OK. | [Approval](/docs/api/objects/approval/) |

**Errors:** `400`, `401`, `403`, `404`, `409`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/approvals/app_01jbx2m9k7q4r8t5v6w3y0z1pr/approve \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "assertion": {
    "challenge_id": "string",
    "credential": {
      "id": "string",
      "rawId": "string",
      "type": "public-key",
      "response": {},
      "authenticatorAttachment": "string",
      "clientExtensionResults": {}
    }
  },
  "note": "string"
}'
```

**Example response**

```json
{
  "id": "apr_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "workspace_id": "string",
  "project_id": "string",
  "environment": "string",
  "status": "pending",
  "action": {
    "kind": "deployment.create",
    "summary": "Deploy web (commit a1b2c3d, \"Fix rota export\") to production",
    "method": "string",
    "path": "string"
  },
  "reason": "string",
  "why_required": [
    {
      "code": "environment_protected",
      "message": "production requires approval for changes made by agents",
      "policy": "guv.environment.protection"
    }
  ],
  "plan": {
    "plan_hash": "sha256:9f2c1e0b7a4d3c2b1a09f8e7d6c5b4a39281706f5e4d3c2b1a0f9e8d7c6b5a49",
    "dry_run": true,
    "summary": "1 to update. +£5.00/month at current usage.",
    "changes": [
      {
        "action": "update",
        "resource": {
          "type": "service",
          "id": "svc_01jbx2m9k7q4r8t5v6w3y0z1sv",
          "name": "web"
        },
        "environment": "production",
        "summary": "Raise web memory limit from 1 GB to 2 GB",
        "diff": [
          {
            "path": "/resources/memory_mb",
            "before": 1024,
            "after": 2048
          }
        ],
        "destructive": false,
        "requires_approval": true,
        "reason": "production requires approval for changes made by agents"
      }
    ],
    "cost": {
      "current_monthly": {
        "amount": "18.20",
        "currency": "GBP",
        "display": "£18.20"
      },
      "projected_monthly": {
        "amount": "23.20",
        "currency": "GBP",
        "display": "£23.20"
      },
      "delta_monthly": {
        "amount": "5.00",
        "currency": "GBP",
        "display": "£5.00"
      },
      "confidence": "medium"
    },
    "policy": {
      "decision": "approval_required",
      "reasons": [
        {
          "code": "environment_protected",
          "message": "production requires approval for changes made by agents",
          "policy": "guv.environment.protection"
        }
      ],
      "approver_roles": [
        "owner",
        "admin"
      ]
    },
    "requires_approval": true,
    "expires_at": "2026-10-23T15:00:00Z"
  },
  "risk": "low",
  "approver_roles": [
    "owner"
  ],
  "requested_by": {
    "type": "agent",
    "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
    "display": "Stewart Webb via agent claude-code-on-stewart-laptop",
    "via_agent": {
      "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
      "name": "claude-code-on-stewart-laptop"
    },
    "on_behalf_of": {
      "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
      "display": "Stewart Webb"
    }
  },
  "decided_by": {
    "type": "agent",
    "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
    "display": "Stewart Webb via agent claude-code-on-stewart-laptop",
    "via_agent": {
      "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
      "name": "claude-code-on-stewart-laptop"
    },
    "on_behalf_of": {
      "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
      "display": "Stewart Webb"
    }
  },
  "decided_at": "2026-10-22T13:05:00Z",
  "decision_note": "string",
  "operation_id": "string",
  "result": {
    "status": 1,
    "body": {}
  },
  "url": "https://guv.dev/",
  "expires_at": "2026-10-22T13:05:00Z",
  "created_at": "2026-10-22T13:05:00Z"
}
```

## Deny a request

```http
POST /v1/approvals/{approval_id}/deny
```

- **Operation:** `denyApproval`
- **Scopes:** `approvals:decide`
- **Idempotency-Key:** required

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `approval_id` * | path | string |  |

**Request body** (`application/json`, required): [ApprovalDeny](/docs/api/objects/approval-deny/)

| Field | Type | Description |
| --- | --- | --- |
| `note` | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | OK. | [Approval](/docs/api/objects/approval/) |

**Errors:** `400`, `401`, `403`, `404`, `409`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/approvals/app_01jbx2m9k7q4r8t5v6w3y0z1pr/deny \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "note": "string"
}'
```

**Example response**

```json
{
  "id": "apr_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "workspace_id": "string",
  "project_id": "string",
  "environment": "string",
  "status": "pending",
  "action": {
    "kind": "deployment.create",
    "summary": "Deploy web (commit a1b2c3d, \"Fix rota export\") to production",
    "method": "string",
    "path": "string"
  },
  "reason": "string",
  "why_required": [
    {
      "code": "environment_protected",
      "message": "production requires approval for changes made by agents",
      "policy": "guv.environment.protection"
    }
  ],
  "plan": {
    "plan_hash": "sha256:9f2c1e0b7a4d3c2b1a09f8e7d6c5b4a39281706f5e4d3c2b1a0f9e8d7c6b5a49",
    "dry_run": true,
    "summary": "1 to update. +£5.00/month at current usage.",
    "changes": [
      {
        "action": "update",
        "resource": {
          "type": "service",
          "id": "svc_01jbx2m9k7q4r8t5v6w3y0z1sv",
          "name": "web"
        },
        "environment": "production",
        "summary": "Raise web memory limit from 1 GB to 2 GB",
        "diff": [
          {
            "path": "/resources/memory_mb",
            "before": 1024,
            "after": 2048
          }
        ],
        "destructive": false,
        "requires_approval": true,
        "reason": "production requires approval for changes made by agents"
      }
    ],
    "cost": {
      "current_monthly": {
        "amount": "18.20",
        "currency": "GBP",
        "display": "£18.20"
      },
      "projected_monthly": {
        "amount": "23.20",
        "currency": "GBP",
        "display": "£23.20"
      },
      "delta_monthly": {
        "amount": "5.00",
        "currency": "GBP",
        "display": "£5.00"
      },
      "confidence": "medium"
    },
    "policy": {
      "decision": "approval_required",
      "reasons": [
        {
          "code": "environment_protected",
          "message": "production requires approval for changes made by agents",
          "policy": "guv.environment.protection"
        }
      ],
      "approver_roles": [
        "owner",
        "admin"
      ]
    },
    "requires_approval": true,
    "expires_at": "2026-10-23T15:00:00Z"
  },
  "risk": "low",
  "approver_roles": [
    "owner"
  ],
  "requested_by": {
    "type": "agent",
    "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
    "display": "Stewart Webb via agent claude-code-on-stewart-laptop",
    "via_agent": {
      "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
      "name": "claude-code-on-stewart-laptop"
    },
    "on_behalf_of": {
      "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
      "display": "Stewart Webb"
    }
  },
  "decided_by": {
    "type": "agent",
    "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
    "display": "Stewart Webb via agent claude-code-on-stewart-laptop",
    "via_agent": {
      "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
      "name": "claude-code-on-stewart-laptop"
    },
    "on_behalf_of": {
      "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
      "display": "Stewart Webb"
    }
  },
  "decided_at": "2026-10-22T13:05:00Z",
  "decision_note": "string",
  "operation_id": "string",
  "result": {
    "status": 1,
    "body": {}
  },
  "url": "https://guv.dev/",
  "expires_at": "2026-10-22T13:05:00Z",
  "created_at": "2026-10-22T13:05:00Z"
}
```

## Withdraw a pending request (requester)

```http
POST /v1/approvals/{approval_id}/cancel
```

- **Operation:** `cancelApproval`
- **Scopes:** any signed-in principal
- **Idempotency-Key:** required

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `approval_id` * | path | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | OK. | [Approval](/docs/api/objects/approval/) |

**Errors:** `400`, `401`, `403`, `404`, `409`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/approvals/app_01jbx2m9k7q4r8t5v6w3y0z1pr/cancel \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
```

**Example response**

```json
{
  "id": "apr_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "workspace_id": "string",
  "project_id": "string",
  "environment": "string",
  "status": "pending",
  "action": {
    "kind": "deployment.create",
    "summary": "Deploy web (commit a1b2c3d, \"Fix rota export\") to production",
    "method": "string",
    "path": "string"
  },
  "reason": "string",
  "why_required": [
    {
      "code": "environment_protected",
      "message": "production requires approval for changes made by agents",
      "policy": "guv.environment.protection"
    }
  ],
  "plan": {
    "plan_hash": "sha256:9f2c1e0b7a4d3c2b1a09f8e7d6c5b4a39281706f5e4d3c2b1a0f9e8d7c6b5a49",
    "dry_run": true,
    "summary": "1 to update. +£5.00/month at current usage.",
    "changes": [
      {
        "action": "update",
        "resource": {
          "type": "service",
          "id": "svc_01jbx2m9k7q4r8t5v6w3y0z1sv",
          "name": "web"
        },
        "environment": "production",
        "summary": "Raise web memory limit from 1 GB to 2 GB",
        "diff": [
          {
            "path": "/resources/memory_mb",
            "before": 1024,
            "after": 2048
          }
        ],
        "destructive": false,
        "requires_approval": true,
        "reason": "production requires approval for changes made by agents"
      }
    ],
    "cost": {
      "current_monthly": {
        "amount": "18.20",
        "currency": "GBP",
        "display": "£18.20"
      },
      "projected_monthly": {
        "amount": "23.20",
        "currency": "GBP",
        "display": "£23.20"
      },
      "delta_monthly": {
        "amount": "5.00",
        "currency": "GBP",
        "display": "£5.00"
      },
      "confidence": "medium"
    },
    "policy": {
      "decision": "approval_required",
      "reasons": [
        {
          "code": "environment_protected",
          "message": "production requires approval for changes made by agents",
          "policy": "guv.environment.protection"
        }
      ],
      "approver_roles": [
        "owner",
        "admin"
      ]
    },
    "requires_approval": true,
    "expires_at": "2026-10-23T15:00:00Z"
  },
  "risk": "low",
  "approver_roles": [
    "owner"
  ],
  "requested_by": {
    "type": "agent",
    "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
    "display": "Stewart Webb via agent claude-code-on-stewart-laptop",
    "via_agent": {
      "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
      "name": "claude-code-on-stewart-laptop"
    },
    "on_behalf_of": {
      "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
      "display": "Stewart Webb"
    }
  },
  "decided_by": {
    "type": "agent",
    "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
    "display": "Stewart Webb via agent claude-code-on-stewart-laptop",
    "via_agent": {
      "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
      "name": "claude-code-on-stewart-laptop"
    },
    "on_behalf_of": {
      "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
      "display": "Stewart Webb"
    }
  },
  "decided_at": "2026-10-22T13:05:00Z",
  "decision_note": "string",
  "operation_id": "string",
  "result": {
    "status": 1,
    "body": {}
  },
  "url": "https://guv.dev/",
  "expires_at": "2026-10-22T13:05:00Z",
  "created_at": "2026-10-22T13:05:00Z"
}
```
