Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.brane.membranelabs.org/llms.txt

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

Allow small refunds automatically. Block large refunds until approval is implemented.

Problem

A support agent can issue refunds to customers. You want to:
  • Allow refunds up to a tenant-specific limit automatically
  • Block refunds above that limit until human approval exists
  • Never allow refunds in non-production environments

Complete Example

from brane import CapabilityDeniedError, Decision, Effect, Runtime

TENANT_LIMITS = {
    "tenant_acme": 100.0,
    "tenant_globex": 250.0,
    "tenant_initech": 50.0,
}

DEFAULT_LIMIT = 100.0

runtime = Runtime(
    agent_id="support-agent",
    environment="prod",
    tenant_id="tenant_acme",
)

@runtime.capability(
    name="refund_customer",
    type="tool",
    risk="high",
    effect=Effect(
        type="financial",
        reversible=False,
        external=True,
        description="Issues a payment refund",
    ),
    data_namespace="billing.refunds",
    owner="payments-team",
)
def refund_customer(customer_id: str, amount_usd: float, reason: str = ""):
    return {"refunded": True, "customer_id": customer_id, "amount": amount_usd}

@runtime.before_capability(
    "refund_customer",
    name="refund_require_prod",
    version="1.0",
    priority=200,
)
def require_prod_for_refunds(ctx):
    if not ctx.is_prod:
        return Decision(
            type="deny",
            reason="Refunds can only be issued in the production environment",
        )
    return Decision(type="allow")

@runtime.before_capability(
    "refund_customer",
    name="refund_amount_limit",
    version="1.0",
    priority=100,
)
def refund_amount_limit(ctx):
    amount = ctx.arg("amount_usd", 0)
    limit = TENANT_LIMITS.get(ctx.tenant_id, DEFAULT_LIMIT)

    if amount <= 0:
        return Decision(type="deny", reason="Refund amount must be positive")

    if amount > limit:
        return Decision(
            type="deny",
            reason=(
                f"Refund of ${amount:.2f} exceeds the ${limit:.2f} limit "
                f"for tenant {ctx.tenant_id}. Human approval is required."
            ),
        )

    return Decision(type="allow")

try:
    result = refund_customer("cust_123", 75.00, reason="Duplicate charge")
    print(result)

    refund_customer("cust_456", 349.00, reason="Defective product")
except CapabilityDeniedError as e:
    print(f"Blocked: {e.reason}")
    print(f"Action ID: {e.action_id}")

How It Works

Two policies protect the refund_customer capability, running in priority order:
  1. require_prod_for_refunds runs first and blocks the refund if the environment is not prod.
  2. refund_amount_limit checks the refund amount against the tenant-specific limit.
Both policies must allow for the refund to proceed. Either can deny.

Adding a Third Policy: Scope Check

@runtime.before_capability(
    "refund_customer",
    name="refund_scope_check",
    version="1.0",
    priority=150,
)
def require_refund_scope(ctx):
    if not ctx.agent_has_scope("refunds:create"):
        return Decision(
            type="deny",
            reason="Agent requires refunds:create scope to issue refunds",
        )
    return Decision(type="allow")

Future: Approval Instead of Deny

Once approval_required decisions are implemented, the limit policy can pause rather than block:
if amount > limit:
    return Decision(
        type="approval_required",
        reason=f"Refund of ${amount:.2f} requires human approval",
        approval={
            "approver_group": "finance-ops",
            "expiration_minutes": 60,
            "context": {
                "customer_id": ctx.arg("customer_id"),
                "amount": amount,
                "tenant": ctx.tenant_id,
            },
        },
    )
The runtime will pause the action, send the approval request, and resume when a human approves or deny automatically when they reject or the request expires.

Production Notes

  • The policy runs before the refund API call. If the policy denies, no refund is issued.
  • Store tenant limits in your configuration or database, not hardcoded.
  • Use action_id from CapabilityDeniedError to correlate the denial with your request logs.
  • When audit sinks are available, every allowed and denied refund attempt will produce an audit event.