Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.svantic.com/llms.txt

Use this file to discover all available pages before exploring further.

Approvals Guide

This guide covers how to configure policies that require human approval, how approvals appear in the dashboard, and how to resolve them from different surfaces.

Prerequisites

  • A running Svantic deployment with Gateway, Mesh, and Dashboard
  • A tenant with at least one registered agent
  • Dashboard access for the tenant

1

Create a Policy That Requires Approval

Svantic ships with default policies for common guard types. To create a custom policy with require_approval enforcement:
curl -X POST https://your-gateway/internal/policies/new \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "bulk_ops_approval",
    "description": "Require human approval for bulk operations exceeding 10 items",
    "scope_type": "tool",
    "enforcement": "require_approval",
    "evaluator": {
      "plugin": "bulk_operation",
      "config": { "threshold": 10 }
    }
  }'
When an agent invokes a tool matching the bulk operation pattern with more than 10 items, the policy engine returns require_approval and the message enters input-required.
2

Trigger an Approval

Send a message that triggers the policy:
curl -X POST https://your-mesh/send \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": "1",
    "method": "message/send",
    "params": {
      "message": {
        "role": "user",
        "parts": [{ "kind": "text", "text": "Delete all expired records from the staging database" }],
        "contextId": "session-123"
      }
    }
  }'
The response indicates the task is in input-required:
{
  "jsonrpc": "2.0",
  "id": "1",
  "result": {
    "id": "task-uuid-456",
    "status": {
      "state": "input-required",
      "message": {
        "role": "agent",
        "parts": [
          {
            "kind": "data",
            "data": {
              "type": "Form",
              "props": {
                "title": "Approval Required",
                "description": "bulk_delete_expired requires approval.",
                "fields": [{ "id": "decision", "type": "action", "options": [
                  { "value": "approve", "label": "Approve" },
                  { "value": "deny", "label": "Deny" }
                ]}]
              }
            },
            "metadata": { "mimeType": "application/json+a2ui" }
          }
        ]
      }
    }
  }
}
3

View Pending Approvals

From the Dashboard

Navigate to Security → Approval Queue. Pending approvals appear as actionable cards with the A2UI form rendered — Approve and Deny buttons for simple approvals, full forms for structured input.

From the API

curl -X POST https://your-gateway/messages/pending \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "tenant_id": "your-tenant" }'
Response:
{
  "messages": [
    {
      "message_id": "task-uuid-456",
      "session_id": "session-123",
      "state": "input-required",
      "user_input": "Delete all expired records from the staging database",
      "task_data": { "status": { "state": "input-required", "..." : "..." } },
      "created_at": "2026-04-07T14:30:00Z"
    }
  ]
}
4

Resolve the Approval

From the Dashboard

Click Approve or Deny in the Approval Queue. The resolution is sent immediately and the agent resumes (or receives a denial).

From the API

curl -X POST https://your-gateway/messages/task-uuid-456/resolve \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "resolution": { "decision": "approve" }
  }'

From Slack

If a Slack notification channel is configured and linked to the policy, the Slack message includes Approve/Deny buttons. Clicking a button sends the resolution through Svantic’s /webhooks/receive/slack endpoint.

From a Webhook Callback

If a webhook notification channel is configured and linked to the policy, the outbound notification includes a callback_token. Use it to resolve without a JWT:
curl -X POST https://your-gateway/messages/task-uuid-456/resolve \
  -H "Content-Type: application/json" \
  -d '{
    "callback_token": "token-from-notification",
    "resolution": { "decision": "approve" }
  }'
5

Permanently Allow a Tool

If you’re tired of approving the same tool every session, update the policy:
curl -X POST https://your-gateway/internal/policies/update \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "policy_id": "bulk_ops_approval",
    "enforcement": "allow"
  }'
This changes the policy from require_approval to allow — the tool will execute without asking. To re-enable approval, update the enforcement back to require_approval.

Structured Input (Beyond Approve/Deny)

Not all approvals are yes/no decisions. An agent may need the user to fill in a form — credentials, categorization choices, configuration parameters. When the A2UI spec contains multiple fields, the resolution includes values for each field:
curl -X POST https://your-gateway/messages/task-uuid-789/resolve \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "resolution": {
      "username": "john@example.com",
      "password": "s3cret",
      "remember_me": true
    }
  }'
The field IDs in the resolution must match the field IDs in the A2UI spec.

Race Conditions

If the same approval is resolved from multiple surfaces simultaneously (e.g., dashboard and Slack), the first resolution wins. Subsequent attempts receive 409 Conflict.

Further Reading