Approvals
Create Human Gates policies, list approval requests, and record approval decisions.
Approvals
Approvals power Human Gates. A policy decides when execution should pause; an approval request is the pending review item created when a policy matches.
Use approval APIs when you build your own SaaS control plane or need programmatic reviewer workflows. Users can manage the same objects from the Weavz UI.
Approval policy object
| Field | Type | Description |
|---|---|---|
id | string | Policy ID |
workspaceId | string | null | Workspace scope. null applies organization-wide. |
name | string | Human-readable policy name |
description | string | null | Optional policy description |
enabled | boolean | Whether the policy can match execution |
priority | number | Lower values run first |
sources | string[] | Execution sources: rest, sdk, mcp_tools, mcp_code, playground, trigger |
mcpServerIds | string[] | Optional MCP server filters |
workspaceIntegrationIds | string[] | Optional configured workspace integration filters |
integrationNames | string[] | Optional integration filters |
integrationAliases | string[] | Optional workspace integration alias filters |
actionNames | string[] | Optional action filters |
connectionStrategies | string[] | Optional fixed, per_user, or per_user_with_fallback filters |
endUserMode | string | any, end_user_present, or no_end_user |
decision | string | require_approval, notify_only, block, or auto_approve |
riskMode | string | rules, always, or model_assisted |
rules | object | Optional rule filters |
approvers | array | Reviewer specification. Supports role reviewers and webhook reviewers. |
timeoutSeconds | number | Pending request lifetime |
defaultOnTimeout | string | reject or expire |
reuseWindowSeconds | number | Optional window for reusing a previous approval decision |
approvalAccessMode | string | dashboard_only, dashboard_and_hosted_link, or hosted_link_only |
metadata | object | Optional policy metadata |
When multiple policies match the exact same action, Weavz applies the highest-priority matching policy. Lower priority values win; ties use the most severe decision, then policy ID. Use one broad policy when a single reviewer decision should cover an action, or split policies by integration alias/action when you need separate approvals.
Create policy
/api/v1/approval-policiesRequest Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
workspaceId | string (uuid) | No | org-wide | Optional workspace scope |
name | string | Yes | - | Policy name, max 120 chars |
description | string | No | - | Optional description, max 1000 chars |
enabled | boolean | No | true | Whether the policy can match execution |
priority | integer | No | 100 | Lower values run first, from 0 to 10000 |
sources | string[] | No | ["rest", "sdk", "mcp_tools", "mcp_code"] | Execution sources: rest, sdk, mcp_tools, mcp_code, playground, trigger |
mcpServerIds | string[] | No | [] | Restrict matching to specific MCP servers |
workspaceIntegrationIds | string[] | No | [] | Restrict matching to configured workspace integrations |
integrationNames | string[] | No | [] | Restrict matching to integration names |
integrationAliases | string[] | No | [] | Restrict matching to workspace integration aliases |
actionNames | string[] | No | [] | Restrict matching to action names |
connectionStrategies | string[] | No | [] | Restrict to fixed, per_user, or per_user_with_fallback |
endUserMode | string | No | any | any, end_user_present, or no_end_user |
decision | string | No | require_approval | require_approval, notify_only, block, or auto_approve |
riskMode | string | No | rules | always, rules, or model_assisted |
rules | object | No | {} | Rule filters. Supported fields include actionCategories, amountThresholds, recipientAllowlist, recipientDenylist, domainAllowlist, domainDenylist, and storagePathPrefixes |
approvers | array | No | [] | Reviewer specs. Use org_role, dashboard_user, end_user, or webhook |
timeoutSeconds | integer | No | 3600 | Pending request lifetime, from 60 seconds to 30 days |
defaultOnTimeout | string | No | reject | reject or expire |
reuseWindowSeconds | integer | No | - | Optional reuse window, from 0 seconds to 7 days |
approvalAccessMode | string | No | dashboard_and_hosted_link | dashboard_only, dashboard_and_hosted_link, or hosted_link_only |
metadata | object | No | - | Optional policy metadata |
Approver shapes
| Type | Required fields | Notes |
|---|---|---|
org_role | roles | Roles can be owner, admin, or member |
dashboard_user | userIds | Dashboard user IDs |
end_user | none | Routes review to the end-user context when available |
webhook | webhookUrl | Must be a public HTTPS URL |
curl -X POST https://api.weavz.io/api/v1/approval-policies \
-H "Authorization: Bearer wvz_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"workspaceId": "550e8400-e29b-41d4-a716-446655440000",
"name": "Review MCP code actions",
"sources": ["mcp_code"],
"decision": "require_approval",
"riskMode": "always",
"approvers": [{ "type": "org_role", "roles": ["owner", "admin"] }],
"timeoutSeconds": 3600
}'Response:
{
"policy": {
"id": "2c7f6e97-6d1e-4c5c-85f3-ffbbfcf404e5",
"name": "Review MCP code actions",
"enabled": true,
"sources": ["mcp_code"],
"decision": "require_approval"
}
}List policies
/api/v1/approval-policiesQuery parameters:
| Parameter | Type | Description |
|---|---|---|
workspaceId | string | Optional workspace filter |
Get policy
/api/v1/approval-policies/:idReturns one approval policy.
curl https://api.weavz.io/api/v1/approval-policies/2c7f6e97-6d1e-4c5c-85f3-ffbbfcf404e5 \
-H "Authorization: Bearer wvz_your_api_key"Update policy
/api/v1/approval-policies/:idSend any policy fields to update.
The update body accepts the same fields as create, all optional. Array filters replace the stored arrays when provided.
curl -X PATCH https://api.weavz.io/api/v1/approval-policies/policy_id \
-H "Authorization: Bearer wvz_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "enabled": false }'Delete policy
/api/v1/approval-policies/:idDeletes the policy. Existing approval request history remains available.
Test policy matching
/api/v1/approval-policies/testUse this endpoint before enabling a policy in production.
{
"policy": {
"name": "Review per-user actions",
"sources": ["sdk"],
"connectionStrategies": ["per_user"],
"decision": "require_approval"
},
"context": {
"workspaceId": "550e8400-e29b-41d4-a716-446655440000",
"source": "sdk",
"integrationName": "gmail",
"integrationAlias": "user_mail",
"actionName": "send_email",
"connectionStrategy": "per_user",
"input": {
"to": "customer@example.com",
"subject": "Follow up"
}
}
}Response:
{
"decision": "require_approval",
"matched": true,
"reasons": [
"source matched sdk",
"connection strategy matched per_user"
]
}Approval request object
| Field | Type | Description |
|---|---|---|
id | string | Approval request ID, prefixed with apr_ |
policyId | string | null | Matched policy |
workspaceId | string | null | Workspace scope |
endUserId | string | null | End user whose connection may be used |
source | string | Execution source |
status | string | pending, approved, rejected, expired, canceled, or consumed |
integrationName | string | Integration name |
integrationAlias | string | null | Workspace integration alias |
actionName | string | Action name |
inputPreview | object | Redacted input preview |
redactedPaths | string[] | Paths hidden from review |
riskReasons | string[] | Policy match reasons |
approvalAccessMode | string | dashboard_only, dashboard_and_hosted_link, or hosted_link_only |
approvalUrl | string | null | Approval URL allowed by the policy mode |
hostedApprovalUrl | string | null | Tokenized hosted approval page when the mode allows hosted links |
idempotencyKey | string | null | Key to reuse when retrying |
expiresAt | string | Expiry timestamp |
Hosted approval links are bearer links. Allow hosted links when an external MCP client, SDK user, or customer-owned app needs a URL it can route to reviewers.
List approval requests
/api/v1/approvalsQuery parameters:
| Parameter | Type | Description |
|---|---|---|
workspaceId | string | Optional workspace filter |
status | string | Optional approval status |
source | string | Optional execution source |
integrationName | string | Optional integration filter |
actionName | string | Optional action filter |
limit | number | Maximum records to return, default 50, up to 100 |
cursor | string | Pagination cursor |
Get approval request
/api/v1/approvals/:idReturns one approval request.
Approve request
/api/v1/approvals/:id/approveApproves a pending request and creates a receipt for the exact retry.
Decision requests accept optional reason and metadata fields. The same body shape is accepted by approve, reject, and cancel endpoints.
curl -X POST https://api.weavz.io/api/v1/approvals/apr_9b36d3f761d84bb2b6f9a0c4b9d1f7e0/approve \
-H "Authorization: Bearer wvz_backend_key" \
-H "Content-Type: application/json" \
-d '{ "reason": "Reviewed customer-facing message." }'The API key used to approve must have Human Gates decision permission for the request's workspace or organization scope.
Reject request
/api/v1/approvals/:id/rejectRejects a pending request. Retrying the original action will create a new approval request if the policy still matches.
Cancel request
/api/v1/approvals/:id/cancelCancels a pending request without approving execution.
Webhook approvers
Webhook approvers let your own app receive approval lifecycle events and route a reviewer to a hosted approval page.
{
"name": "Review agent sends",
"workspaceId": "550e8400-e29b-41d4-a716-446655440000",
"sources": ["sdk", "mcp_tools", "mcp_code"],
"decision": "require_approval",
"riskMode": "always",
"approvers": [
{
"type": "webhook",
"webhookUrl": "https://app.example.com/weavz/approvals"
}
]
}Webhook URLs must be public HTTPS URLs.
Events:
| Event | When it is sent |
|---|---|
approval.requested | A new approval request is created |
approval.approved | A reviewer approves the request |
approval.rejected | A reviewer rejects the request |
approval.canceled | A reviewer cancels the request |
approval.expired | A pending request is marked expired |
Payload example:
{
"event": "approval.requested",
"createdAt": "2026-05-14T12:00:00.000Z",
"approval": {
"id": "apr_9b36d3f761d84bb2b6f9a0c4b9d1f7e0",
"status": "pending",
"workspaceId": "550e8400-e29b-41d4-a716-446655440000",
"source": "mcp_code",
"integrationName": "gmail",
"integrationAlias": "customer_mail",
"actionName": "send_email",
"inputPreview": {
"to": "customer@example.com",
"subject": "Follow up",
"apiKey": "[REDACTED]"
},
"redactedPaths": ["apiKey"],
"riskReasons": ["source matched mcp_code"],
"approvalUrl": "https://platform.weavz.io/approvals/apr_9b36d3f761d84bb2b6f9a0c4b9d1f7e0",
"hostedApprovalUrl": "https://platform.weavz.io/approve/apr_9b36d3f761d84bb2b6f9a0c4b9d1f7e0?token=apr_link_...",
"api": {
"detailUrl": "https://api.weavz.io/api/v1/approvals/apr_9b36d3f761d84bb2b6f9a0c4b9d1f7e0",
"approveUrl": "https://api.weavz.io/api/v1/approvals/apr_9b36d3f761d84bb2b6f9a0c4b9d1f7e0/approve",
"rejectUrl": "https://api.weavz.io/api/v1/approvals/apr_9b36d3f761d84bb2b6f9a0c4b9d1f7e0/reject",
"cancelUrl": "https://api.weavz.io/api/v1/approvals/apr_9b36d3f761d84bb2b6f9a0c4b9d1f7e0/cancel"
},
"codeRun": {
"type": "mcp_code_batch_run",
"codeRunId": "crun_4f7b...",
"codeHash": "sha256...",
"toolSurfaceHash": "sha256...",
"approvalGroups": [
{
"approvalPolicyId": "pol_...",
"approvalPolicyName": "Review customer-visible sends",
"tools": []
}
],
"analysisConfidence": "exact",
"dynamicSignals": [],
"continuation": {
"tool": "weavz_execute",
"arguments": { "approvalId": "apr_9b36d3f761d84bb2b6f9a0c4b9d1f7e0" }
}
},
"idempotencyKey": "ticket-421-send",
"expiresAt": "2026-05-14T13:00:00.000Z"
},
"receipt": null
}Delivery headers:
| Header | Description |
|---|---|
X-Weavz-Event | Lifecycle event name |
X-Weavz-Delivery | Unique delivery ID |
X-Weavz-Attempt | Delivery attempt number |
X-Weavz-Approval | Approval request ID |
X-Weavz-Timestamp | Unix timestamp when webhook signing is enabled |
X-Weavz-Signature | t=<timestamp>,v1=<hmac> when webhook signing is enabled |
For signed deliveries, verify the HMAC over <timestamp>.<raw JSON body> and reject stale timestamps. The webhook payload includes the redacted action preview only; raw action input and connection credentials are never sent.
Developer platforms can use webhook approvers as approval callbacks: store approval.id, render approval.inputPreview and approval.codeRun in their own UI, then call approval.api.approveUrl or approval.api.rejectUrl with an API key that has approvals.decide. API decision URLs are not bearer URLs; they still require normal API authentication.
For MCP Code Mode, approval requests may represent a batch run instead of one explicit tool call. In that case approval.inputPreview has type: "mcp_code_batch_run" and includes deterministic reviewer context such as codeRunId, intentSummary, impactSummary, predictedTools, approvalRequiredTools, approvalGroups, availableApps, externalDomains, storageIndicators, analysisConfidence, dynamicSignals, codeHash, and toolSurfaceHash. codeRunId is stable across approval requests for the same stored Code Mode run, so customer-owned approval UIs can group sibling approvals without inferring from hashes. approvalGroups shows which policy decision covers which predicted calls. If a Code Mode run needs multiple approvals, the MCP response includes an approvals array and an approvalGroup summary; after each decision, call weavz_execute or /api/v1/mcp/servers/:id/execute-code with only { "approvalId": "apr_..." } and Weavz will return the next pending approval or the final result.
The approval detail API also includes recent webhookDeliveries when lifecycle notifications were configured, so customer dashboards can show whether callbacks were sent, failed, or retried.
SDK resources
TypeScript:
const { policies } = await client.approvalPolicies.list({ workspaceId })
const { policy } = await client.approvalPolicies.create({ name, sources: ['mcp_tools'] })
const { approvals } = await client.approvals.list({ status: 'pending' })
await client.approvals.approve(approvalId, { reason: 'Reviewed.' })
await client.approvals.wait(approvalId)Python:
policies = client.approval_policies.list(workspace_id=workspace_id)
created = client.approval_policies.create(name="Review MCP", sources=["mcp_tools"])
pending = client.approvals.list(status="pending")
client.approvals.approve(approval_id, reason="Reviewed.")
client.approvals.wait(approval_id)