decision_request_approval

Requests human approval for a decision that requires exception handling. This is typically used when a policy evaluation returns requires_exception.

Overview

The decision_request_approval method sends an approval request to human reviewers. Approval requests are delivered through configured approval routes (email, Slack, webhooks, etc.) and can be approved or rejected through the TraceMem dashboard or API.

Request

Parameters

ParameterTypeRequiredDescription
decision_idstringYesThe decision ID (from decision_create)
titlestringNoApproval request title (brief summary)
messagestringNoDetailed explanation for the approver
approval_route_idstringNoSpecific approval route to use (usually derived from policy if not specified)

Example Request

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "decision_request_approval",
    "arguments": {
      "decision_id": "TMEM_abc123...",
      "title": "High-Value Discount Approval",
      "message": "Customer 1001 requesting 25% discount on $10k order. Customer has been with us for 5 years with perfect payment history. Policy evaluation returned 'requires_exception'.",
      "approval_route_id": "route_discount_manager"
    }
  }
}

Response

Success Response

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"approval_id\": \"appr_...\", \"status\": \"pending\", \"delivery\": {\"status\": \"pending\", \"note\": \"Approval will be delivered by worker service\"}}"
      }
    ]
  }
}

Response Fields

FieldTypeDescription
approval_idstringUnique identifier for this approval request
statusstringCurrent approval status: "pending", "approved", or "rejected"
deliveryobjectDelivery status information
delivery.statusstringDelivery status: "pending", "delivered", or "failed"
delivery.notestringAdditional information about delivery

Example Response

json
{
  "approval_id": "appr_abc123",
  "status": "pending",
  "delivery": {
    "status": "pending",
    "note": "Approval will be delivered by worker service"
  }
}

Approval Status

After requesting approval, the decision status changes to needs_approval. You can check the approval status by:

  1. Polling decision_get to check if status is "approved" or if the decision was rejected
  2. The decision will remain in needs_approval status until approved or rejected

Error Cases

Missing Decision ID

Error Code: -32600 (Invalid Request)

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32600,
    "message": "Invalid Request",
    "data": "decision_id is required"
  }
}

Decision Not Found

Error Code: -32603 (Internal error)

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error",
    "data": "Decision not found"
  }
}

Approval Route Not Found

Error Code: -32603 (Internal error)

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error",
    "data": "Approval route 'route_discount_manager' not found or not accessible"
  }
}

Common causes:

  • Approval route doesn't exist
  • Agent doesn't have permission to use this approval route
  • Approval route ID is misspelled

Permission Denied

HTTP Status: 403 Forbidden

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error",
    "data": "Permission denied: agent does not have permission to request approvals"
  }
}

Authentication Errors

HTTP Status: 401 Unauthorized

Occurs when:

  • API key is missing
  • API key is invalid
  • API key has been revoked

Usage Examples

Request Approval After Policy Evaluation

python
import requests
import json
import time

def request_approval(decision_id, title=None, message=None, approval_route_id=None, api_key=None):
    args = {"decision_id": decision_id}
    if title:
        args["title"] = title
    if message:
        args["message"] = message
    if approval_route_id:
        args["approval_route_id"] = approval_route_id
    
    response = requests.post('https://mcp.tracemem.com',
        headers={'Authorization': f'Agent {api_key}'},
        json={
            "jsonrpc": "2.0",
            "id": 1,
            "method": "tools/call",
            "params": {
                "name": "decision_request_approval",
                "arguments": args
            }
        })
    
    result = response.json()
    if "error" in result:
        raise Exception(result["error"]["data"])
    
    approval_data = json.loads(result["result"]["content"][0]["text"])
    return approval_data

# Evaluate policy
policy_result = evaluate_policy(
    decision_id=decision_id,
    policy_id="discount_cap_v1",
    inputs={"proposed_discount": 0.25, "customer_tier": "standard"},
    api_key=api_key
)

if policy_result["outcome"] == "requires_exception":
    # Request approval
    approval = request_approval(
        decision_id=decision_id,
        title="Discount Approval Required",
        message=f"Customer requesting 25% discount. {policy_result['rationale']['message']}",
        api_key=api_key
    )
    
    print(f"Approval requested: {approval['approval_id']}")
    
    # Poll for approval
    for _ in range(30):  # Poll up to 30 times
        time.sleep(2)
        status = get_decision_status(decision_id, api_key)
        
        if status["status"] == "approved":
            print("Approval granted!")
            # Proceed with action
            break
        elif status["status"] == "aborted":
            print("Approval rejected")
            # Handle rejection
            break

Complete Approval Workflow

python
# Create decision
decision = create_decision(
    intent="customer.discount.evaluate",
    automation_mode="propose",
    api_key=api_key
)
decision_id = decision["decision_id"]

# Read customer data
customer = read_data(
    decision_id=decision_id,
    product="pg_customers_v1",
    purpose="discount_evaluation",
    query={"customer_id": "1001"},
    api_key=api_key
)

# Evaluate policy
policy_result = evaluate_policy(
    decision_id=decision_id,
    policy_id="discount_cap_v1",
    inputs={
        "proposed_discount": 0.25,
        "customer_tier": customer["records"][0]["tier"],
        "order_value": 10000
    },
    api_key=api_key
)

# Handle policy outcome
if policy_result["outcome"] == "allow":
    # Proceed without approval
    apply_discount(decision_id, api_key)
    close_decision(decision_id, "commit", api_key=api_key)
    
elif policy_result["outcome"] == "deny":
    # Reject immediately
    close_decision(decision_id, "abort", 
                  reason=policy_result["rationale"]["message"],
                  api_key=api_key)
    
elif policy_result["outcome"] == "requires_exception":
    # Request approval
    approval = request_approval(
        decision_id=decision_id,
        title="High-Value Discount Approval",
        message=f"""
        Customer: {customer['records'][0]['name']} (ID: {customer['records'][0]['customer_id']})
        Requested Discount: 25%
        Order Value: $10,000
        Customer Tier: {customer['records'][0]['tier']}
        Policy Rationale: {policy_result['rationale']['message']}
        """,
        api_key=api_key
    )
    
    # Poll for approval
    approved = wait_for_approval(decision_id, timeout=3600, api_key=api_key)
    
    if approved:
        apply_discount(decision_id, api_key)
        close_decision(decision_id, "commit", api_key=api_key)
    else:
        close_decision(decision_id, "abort", 
                      reason="Approval was rejected",
                      api_key=api_key)

def wait_for_approval(decision_id, timeout=3600, api_key=None):
    """Wait for approval with timeout"""
    start_time = time.time()
    while time.time() - start_time < timeout:
        status = get_decision_status(decision_id, api_key)
        
        if status["status"] == "approved":
            return True
        elif status["status"] == "aborted":
            return False
        
        time.sleep(5)  # Poll every 5 seconds
    
    raise TimeoutError("Approval request timed out")

Best Practices

  1. Provide clear context: Include all relevant information in the message field to help approvers make informed decisions

  2. Use descriptive titles: Make titles concise but informative (e.g., "25% Discount for Premium Customer")

  3. Include policy rationale: When requesting approval after requires_exception, include the policy's rationale message

  4. Poll appropriately: Don't poll too frequently (every 2-5 seconds is reasonable)

  5. Set timeouts: Don't wait indefinitely for approval; set a reasonable timeout

  6. Handle all outcomes: Be prepared for approval, rejection, or timeout

  7. Log approval requests: Keep track of approval requests for debugging and audit purposes

  8. Use specific approval routes: If you know which approval route to use, specify it explicitly

Important Notes

  • Decision status changes: After requesting approval, the decision status changes to needs_approval

  • Approval delivery: Approvals are delivered asynchronously through configured routes (email, Slack, webhooks, etc.)

  • Self-approval not allowed: Agents cannot approve their own requests

  • Approval routes: Approval routes are configured by administrators and determine how approvals are delivered

  • Expiration: Approval requests may expire if not acted upon within a configured time period

  • Single approval per decision: Typically, only one approval request can be active per decision

  • Approval is required: If a decision requires approval, writes and other operations may be blocked until approval is granted

Related Methods

TraceMem is trace-native infrastructure for AI agents