Skip to main content
Spending policies are the core of Sardis’s safety system. Every payment an AI agent makes must pass through a policy check before execution. Policies are written in natural language and automatically enforced.

Natural Language Policy Creation

1

Write Policy in Plain English

Sardis uses NLP to parse spending rules written in natural language:
from sardis import SardisClient

client = SardisClient(api_key="sk_...")

wallet = client.wallets.create(
    name="research-agent",
    chain="base",
    policy="""
    Max $50 per transaction.
    Max $500 per day.
    Only allow payments to openai.com, anthropic.com, and arxiv.org.
    Block gambling and adult content merchants.
    """
)
2

Policy is Automatically Parsed

The natural language parser converts your policy into enforcement rules:
# Behind the scenes, this becomes:
# - per_transaction_limit: $50
# - daily_limit: $500  
# - merchant_allowlist: ["openai.com", "anthropic.com", "arxiv.org"]
# - blocked_mccs: [7995, 5967]  # Gambling, adult content
3

Test Your Policy

Validate the policy before deploying:
# Test policy validation
result = client.policies.validate(
    wallet_id=wallet.wallet_id,
    amount=25.00,
    merchant="openai.com",
    token="USDC"
)

print(f"Approved: {result.approved}")
print(f"Reason: {result.reason}")

Policy Validation and Testing

Before making real payments, test your policy:
# Test a valid payment
test_cases = [
    {"amount": 25, "merchant": "openai.com", "expected": True},
    {"amount": 100, "merchant": "openai.com", "expected": False},  # Exceeds per-tx limit
    {"amount": 25, "merchant": "gambling.com", "expected": False},  # Blocked merchant
]

for test in test_cases:
    result = client.policies.validate(
        wallet_id=wallet.wallet_id,
        amount=test["amount"],
        merchant=test["merchant"],
        token="USDC"
    )
    
    assert result.approved == test["expected"], f"Policy test failed: {test}"
    print(f"✓ Test passed: ${test['amount']} to {test['merchant']}")

Dry Run Mode

Test policies without creating wallets:
from sardis_v2_core.nl_policy_parser import parse_natural_language_policy

policy_text = "Max $100/tx, $1000/month, only allow AWS and OpenAI"
policy = parse_natural_language_policy(policy_text)

print(f"Per-tx limit: ${policy.per_transaction_limit}")
print(f"Monthly limit: ${policy.monthly_limit}")
print(f"Allowlist: {policy.merchant_allowlist}")

Advanced Policy Rules

Merchant Allowlist

Restrict spending to specific merchants:
wallet = client.wallets.create(
    name="api-agent",
    chain="base",
    policy="""
    Max $200 per transaction.
    Only allow payments to:
    - openai.com
    - anthropic.com
    - aws.amazon.com
    - stripe.com
    """
)
Any payment to a merchant not on the list will be rejected with:
PolicyViolation: Merchant 'unknown-site.com' not in allowlist

Time Window Limits

Set rolling time-based limits:
wallet = client.wallets.create(
    name="shopping-agent",
    chain="base",
    policy="""
    Max $100 per transaction.
    Max $500 per day.
    Max $2,000 per week.
    Max $8,000 per month.
    """
)
How time windows work:
  • Daily: Rolling 24-hour window from first spend
  • Weekly: Rolling 7-day window
  • Monthly: Rolling 30-day window
Windows reset automatically when they expire.

MCC Blocking

Block entire merchant categories using Merchant Category Codes:
wallet = client.wallets.create(
    name="safe-agent",
    chain="base",
    policy="""
    Max $1000 per day.
    Block gambling merchants.
    Block adult content.
    Block cryptocurrency purchases.
    Block weapons and ammunition.
    """
)
Common blocked categories:
CategoryMCCExample
Gambling7995Online casinos, betting
Adult Content5967Adult websites
Crypto6051Crypto exchanges
Weapons5091Gun stores
Tobacco5993Cigarettes, vaping

Approval Thresholds

Require human approval for large transactions:
wallet = client.wallets.create(
    name="procurement-agent",
    chain="base",
    policy="""
    Max $100 per transaction without approval.
    Max $5,000 per transaction with approval.
    Max $20,000 per month.
    """
)

# Payments over $100 will trigger approval flow
result = wallet.pay(to="aws.amazon.com", amount=250, token="USDC")

if result.status == "pending_approval":
    print(f"Approval required: {result.approval_url}")
    print(f"Approval ID: {result.approval_id}")
    
    # Approve via dashboard or API
    client.approvals.approve(
        approval_id=result.approval_id,
        approver_id="user_123"
    )

Spending Scopes

Limit spending to specific categories:
wallet = client.wallets.create(
    name="compute-agent",
    chain="base",
    policy="""
    Max $1000 per day.
    Only allow compute and data spending.
    """
)
Available scopes:
  • all - No restrictions (default)
  • compute - Cloud compute (AWS, GCP, Azure)
  • data - Data services (APIs, databases)
  • services - SaaS subscriptions
  • retail - E-commerce purchases
  • digital - Digital goods
  • agent_to_agent - Only agent-to-agent payments

Per-Merchant Limits

Set different limits for different merchants:
from sardis_v2_core.spending_policy import SpendingPolicy, MerchantRule
from decimal import Decimal

policy = SpendingPolicy(
    per_transaction_limit=Decimal("500"),
    total_limit=Decimal("10000"),
    merchant_rules=[
        MerchantRule(
            merchant="openai.com",
            rule_type="allow",
            per_merchant_limit=Decimal("200")
        ),
        MerchantRule(
            merchant="aws.amazon.com",
            rule_type="allow",
            per_merchant_limit=Decimal("1000")
        ),
    ]
)

wallet = client.wallets.create(
    name="api-agent",
    chain="base",
    policy=policy  # Pass policy object directly
)

Policy Enforcement Flow

Every payment goes through these checks (in order):
┌─────────────────────────────────────────┐
│  1. Amount Validation                    │
│     ✓ Amount > 0                         │
│     ✓ Fee >= 0                           │
├─────────────────────────────────────────┤
│  2. Spending Scope Check                 │
│     ✓ Category allowed?                  │
├─────────────────────────────────────────┤
│  3. MCC Check                            │
│     ✓ Merchant category not blocked?     │
├─────────────────────────────────────────┤
│  4. Per-Transaction Limit                │
│     ✓ Amount <= per_tx_limit?            │
├─────────────────────────────────────────┤
│  5. Total Spending Limit                 │
│     ✓ Cumulative < total_limit?          │
├─────────────────────────────────────────┤
│  6. Time Window Limits                   │
│     ✓ Daily/weekly/monthly OK?           │
├─────────────────────────────────────────┤
│  7. Balance Check                        │
│     ✓ Wallet has enough funds?           │
├─────────────────────────────────────────┤
│  8. Merchant Rules                       │
│     ✓ Allowlist/blocklist/caps           │
├─────────────────────────────────────────┤
│  9. Goal Drift Detection                 │
│     ✓ Agent behavior as expected?        │
├─────────────────────────────────────────┤
│ 10. Approval Threshold                   │
│     ✓ Needs human approval?              │
└─────────────────────────────────────────┘
The first failure short-circuits with a denial reason.

Updating Policies

Policies can be updated at any time:
# Update an existing wallet's policy
client.wallets.update_policy(
    wallet_id=wallet.wallet_id,
    policy="""
    Max $200 per transaction.
    Max $2000 per day.
    Only allow openai.com, anthropic.com, aws.amazon.com.
    """
)

# Or update with a policy object
from sardis_v2_core.spending_policy import SpendingPolicy
from decimal import Decimal

new_policy = SpendingPolicy(
    per_transaction_limit=Decimal("200"),
    daily_limit=Decimal("2000"),
    merchant_allowlist=["openai.com", "anthropic.com", "aws.amazon.com"]
)

client.wallets.update_policy(
    wallet_id=wallet.wallet_id,
    policy=new_policy
)
Policy updates take effect immediately. Any pending transactions will be re-evaluated against the new policy.

Policy Monitoring

Track policy violations and spending patterns:
# Get policy violations
violations = client.policies.get_violations(
    wallet_id=wallet.wallet_id,
    start_date="2026-03-01",
    end_date="2026-03-31"
)

for v in violations:
    print(f"{v.timestamp}: {v.reason}")
    print(f"  Attempted: ${v.amount} to {v.merchant}")

# Get spending summary
summary = client.wallets.get_spending_summary(wallet_id=wallet.wallet_id)
print(f"Spent today: ${summary.spent_today}")
print(f"Spent this week: ${summary.spent_week}")
print(f"Remaining daily: ${summary.remaining_daily}")

Troubleshooting

Check that your policy text is clear and unambiguous. Common issues:
  • Using inconsistent currency symbols ($100 vs 100 USD)
  • Ambiguous time periods (“weekly” vs “per week”)
  • Conflicting rules (allowlist and blocklist for same merchant)
Use the validation endpoint to see parsed policy:
parsed = client.policies.parse(
    policy_text="Max $100/tx, $1000/day"
)
print(parsed.to_json())
Check the policy violation reason:
result = wallet.pay(to="merchant.com", amount=50, token="USDC")
if not result.success:
    print(f"Blocked: {result.policy_result.reason}")
    print(f"Failed checks: {result.policy_result.checks_failed}")
Common causes:
  • Merchant not in allowlist
  • Daily/weekly limit reached
  • Merchant MCC is blocked
Set up webhook alerts for violations:
client.webhooks.create(
    url="https://yourapp.com/webhooks/policy-violation",
    events=["policy.violation", "policy.threshold_warning"]
)
Then in your webhook handler:
@app.route("/webhooks/policy-violation", methods=["POST"])
def handle_violation():
    event = request.json
    if event["type"] == "policy.violation":
        # Alert your team
        send_slack_alert(
            f"Policy violation: {event['data']['reason']}"
        )
    return {"status": "ok"}
No. Policies are always enforced. This is a core security feature.If you need to make an exception, update the policy temporarily:
# Temporarily increase limit
client.wallets.update_policy(
    wallet_id=wallet.wallet_id,
    policy="Max $10,000 per transaction"  # Higher limit
)

# Make payment
result = wallet.pay(to="merchant.com", amount=5000, token="USDC")

# Restore original policy
client.wallets.update_policy(
    wallet_id=wallet.wallet_id,
    policy=original_policy
)

Next Steps

Making Payments

Execute payments with policy enforcement

Webhooks

Get notified of policy violations

Compliance & KYC

Add compliance checks to policies

Testing

Test policies in sandbox mode

Build docs developers (and LLMs) love