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. Dashboard users can manage the same objects from Approvals.
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 |
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 |
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 |
approvalAccessMode | string | dashboard_only, dashboard_and_hosted_link, or hosted_link_only |
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-policiescurl -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 |
Update policy
/api/v1/approval-policies/:idSend any policy fields to update.
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": "[email protected]",
"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 |
dashboardUrl | string | null | Dashboard approval page when the mode allows dashboard access |
idempotencyKey | string | null | Key to reuse when retrying |
expiresAt | string | Expiry timestamp |
Hosted approval links are bearer links. Use dashboard_only for stricter review flows, or 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, up to 100 |
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.
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 dashboard/API 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": "[email protected]",
"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",
"approvalAccessMode": "dashboard_and_hosted_link",
"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 approvals with different access modes, 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)