Skip to content

A policy defines when a tool is allowed to execute.

Every request made through Swytchcode is evaluated against your configured policies before an API call is made. If a policy condition matches, Swytchcode applies the configured action immediately. Response policies run once more after the call, before the agent sees the result.

Policies allow you to enforce business rules, security requirements, and operational constraints without changing your application code.


How Policy Evaluation Works

Whenever a tool is executed, Swytchcode performs the following checks:

Tool Execution Requested
│
▼
MODIFY policies change the call (if any match)
│
▼
Evaluate the other policies on the (changed) call
│
▼
Any block? ──Yes──▶ Blocked (exit code 6)
│No
▼
Any approval? ──Yes──▶ Held for a person (exit code 7)
│No
▼
Call is sent
│
▼
Response policies: hide, cut, or withhold the response
│
▼
Result returned to the agent

A policy only applies to the methods listed in its target. When several policies match, a block wins over an approval, and an approval over an ALLOW. With "on_no_match": "deny", a call no ALLOW policy matches is blocked, which turns the file into an allowlist.


Anatomy of a Policy

Every policy consists of four parts:

Policy
│
├── Target
├── Condition
├── Action
└── Identifier

Each part serves a specific purpose.

ComponentPurposeJSON field
IDUniquely identifies the policy.id
TargetSpecifies which methods the policy applies to.target
ConditionDetermines when the rule should trigger.when
ActionDefines what happens when the condition is met.action

Put together, a policy in .swytchcode/integrations/policies.json looks like this:

{
"id": "block-large-payments",
"target": ["stripe.payment_intent.create"],
"when": {
"field": "amount",
"operator": ">",
"value": 100000
},
"action": {
"type": "POLICY_BLOCKED",
"message": "Payments above the allowed limit are blocked."
}
}

If amount on the request exceeds 100000, swy exec (and the equivalent swytchcode_exec MCP call) stops before any network request is made, returns exit code 6, and returns an error naming the policy and its message. To hold a command until a person confirms it, use human approval (REQUIRES_APPROVAL) instead of a hard block.

Besides blocking, a policy can:

  • Change the call (MODIFY) - for example, cap a page size instead of failing.
  • Allow it (ALLOW) - with "on_no_match": "deny", only allowed calls run.
  • Hide or cut the response (REDACT, TRUNCATE) - for example, replace card numbers with [REDACTED] before the agent sees them.
  • Only watch ("mode": "monitor") - record matches without acting, to try a rule first.

These need a version-2 file. See Action Types for the full list.


Choosing a Target

Policies only evaluate requests that match one of their targets. target is always an array of canonical IDs.

{ "target": ["github.repo.star"] }

only evaluates requests that star GitHub repositories.

Another policy might target:

{ "target": ["stripe.payment.create"] }

to validate payment requests.

A single policy can protect multiple methods if they share the same business rule.

{
"target": [
"stripe.payment.create",
"stripe.payment.refund",
"stripe.payment.capture"
]
}

In a version-2 file, a target can be a pattern: "stripe.*" guards every Stripe method, and "*" every tool. Combined with built-in fields such as $http_method, one policy can cover a whole class of calls - for example, every DELETE in production.


Writing Conditions

Conditions determine when a policy should apply.

A condition evaluates information from the request being executed, and - in a version-2 file - about the call itself and earlier calls.

Examples include:

  • Payment amount
  • Currency
  • Email address
  • Branch name
  • Environment
  • Region
  • Request metadata

A simple condition might read:

Block any payment greater than 10,000.

which is written as:

{ "field": "amount", "operator": ">", "value": 10000 }

Another might read:

Block requests that aren’t from a company email address.

{ "field": "email", "operator": "not_contains", "value": "@yourcompany.com" }

A version-2 file adds more to compare against:

  • Nested fields: customer.email, items[*].price.
  • Built-in fields that Swytchcode fills in, which an agent can’t set: $http_method, $env, $host, $time, $weekday, $caller, and $email_domains (the domain of every address anywhere in the request), among others.
  • Lists kept in one place: {"$list": "blocked_domains"}.
  • Earlier calls: rate (calls per window), sum (e.g. daily spend), repeat, called_before, returned_by.
  • Sensitive data: contains_pii and contains_secret.
{ "field": "$email_domains", "operator": "in", "value": ["gmail.com", "yahoo.com"], "ignore_case": true }

Policies should describe business intent rather than implementation details. The full set of supported operators - equality/ordering (==, !=, >, >=, <, <=), membership (in, not_in), substring (contains, not_contains), string matching (starts_with, ends_with, matches), presence (exists, not_exists, empty, not_empty), and time comparisons (before, after, between, outside) - is documented in the policies.json reference, with the version-2 built-in fields, condition options, and conditions on earlier calls.


Combining Conditions

Many production rules require more than one condition.

Swytchcode lets you combine leaf conditions (field/operator/value) into a group condition using operator: "all" | "any" | "not" plus a conditions array.

all (AND) - every condition must match:

{
"operator": "all",
"conditions": [
{ "field": "amount", "operator": ">", "value": 10000 },
{ "field": "currency", "operator": "==", "value": "usd" }
]
}

any (OR) - at least one condition must match:

{
"operator": "any",
"conditions": [
{ "field": "role", "operator": "==", "value": "admin" },
{ "field": "role", "operator": "==", "value": "support" }
]
}

not - inverts the nested condition:

{
"operator": "not",
"conditions": [
{ "field": "test_mode", "operator": "exists" }
]
}

Groups can nest inside groups, so you can build rules like “amount over 10,000 AND currency is USD or EUR AND NOT test mode” as a single when block. See Group Conditions in the policies.json reference for a full worked example.


Common Policy Patterns

The most effective policies are usually simple. Policy recipes has a ready-to-use policy for each of the patterns below.

Spending Limits

Prevent expensive operations.

Examples:

  • Payments above a threshold
  • Refund limits
  • Credit issuance limits

Production Protection

Prevent accidental changes to production systems.

Examples:

  • Block delete operations
  • Prevent destructive workflows
  • Disable infrastructure changes

Organization Rules

Restrict operations based on company policies.

Examples:

  • Company email domains only
  • Approved GitHub organizations
  • Approved cloud projects

Environment Restrictions

Different environments often require different rules.

For example:

Development:

  • Relaxed limits
  • Test data
  • Sandbox providers

Production:

  • Strict validation
  • Protected resources
  • Higher security

Time-Based Rules

Restrict execution during specific time windows.

Examples:

  • Business hours only
  • Maintenance windows
  • Scheduled deployments

Designing Good Policies

When writing policies, follow these principles.

Keep policies focused

Instead of one large policy:

❌

Everything Policy

Prefer:

✅

Payment Limit
Repository Protection
Production Restriction
Email Validation

Smaller policies are easier to review and maintain.


Write descriptive IDs

Good:

block-production-deletes

Better than:

policy-4

Policy IDs should describe their purpose.


Avoid deeply nested logic

If a policy becomes difficult to understand, consider splitting it into multiple policies.

Simple policies are easier to debug and less likely to introduce unexpected behavior.


Prefer allowlists

Instead of trying to block every unsafe value:

Block:
A
B
C
D
E

Prefer explicitly allowing trusted values.

Allow:
Production
Staging

Allowlists are easier to reason about and generally more secure.


Validating Policies

Before deploying policies, always validate them.

Terminal window
swy policy validate

Validation checks for:

  • Invalid operators
  • Missing required fields
  • Duplicate IDs
  • Empty targets
  • Invalid value types
  • Unknown action types
  • Incorrect condition structures
  • Values that can’t match the field’s type in the tool’s input schema

It also runs the tests written in the file. Fix validation errors before running production workloads: while policies.json is invalid, every call is stopped.


Testing Policies

Policies should be tested before deployment.

A typical workflow looks like this:

Write Policy
│
▼
Validate Policy
│
▼
Test in Sandbox
│
▼
Review Behavior
│
▼
Deploy to Production

Three tools help:

Tests in the file. In a version-2 file, each policy can carry example calls and what should happen to them. swy policy validate runs them against the whole file and fails if one doesn’t match, so they can run in CI:

"tests": [
{ "args": { "amount": 250000 }, "expect": "block" },
{ "args": { "amount": 500 }, "expect": "allow" }
]

Tests can also give earlier calls (for rate limits), the expected changed arguments of a MODIFY, or a response for response policies. They never send, count, or log anything. See Tests.

Dry runs. --dry-run runs every policy check on a real request without making the API call:

Terminal window
swy policy validate
swy exec <canonical_id> --dry-run

Monitor mode. "mode": "monitor" lets a new rule watch real traffic without blocking. Matches show up as monitored in swy audit policy; switch to enforcing once the rule catches exactly what you meant.

Once a policy is live, swy audit policy shows you which requests it has actually blocked, changed, or hidden, so you can tell a too-strict rule from a working one.


Best Practices

  • Write one policy per business rule.
  • Use descriptive policy IDs.
  • Prefer allowlists whenever possible.
  • Keep conditions simple and readable.
  • Validate policies after every change.
  • Test policies in sandbox environments before production.
  • Review policies alongside application code during code review.
  • Add tests to each policy and run swy policy validate in CI.
  • Try new rules in monitor mode first.
  • Remove unused policies to keep configurations easy to maintain.