Policies
A policy adds your own limits to Warns' checks: how much one payment may cost, how much an agent may spend per hour, day or week, when it may pay, and rules for the cases you care about. Every new agent gets a starter policy. You edit policies in Policies, as JSON.
The starter policy
When you create an agent, the form asks for two numbers, a cap per payment and a daily cap, and Warns writes this policy for the agent:
{ "name": "Research agent limits", "scope": { "agentIds": ["research-agent"] }, "limits": { "perAction": { "usd": 0.5 }, "windows": [{ "kind": "day", "usd": 10 }] }, "rules": [ { "id": "review-new-destination", "if": { "all": [ { "signal": "NEW_DESTINATION" }, { "field": "amountUsd", "op": "gt", "value": 0.1 } ] }, "effect": "review", "reason": "First payment to this destination needs approval" } ]}
With the form's defaults: at most 0.50 USDC per payment and 10 per day, and a first payment to a new address above 0.10 is held for review.
Editing policies
- Policies lists the policies of the current environment, Test or Live.
- Saving never overwrites: each save creates a new version and makes it active. You can load an older version and make it active again.
- Two presets help you start: Safe research agent and High volume API agent.
- A policy that does not validate is not saved. The error says where: the path in the JSON, then the problem.
Fields
| Field | What it does |
|---|---|
name | A label, up to 128 characters. |
scope.agentIds | The agents it applies to. Leave it out to apply the policy to every agent of the environment. |
limits.perAction.usd | The most one payment may cost. Above it, the payment is blocked with AMOUNT_ABOVE_ACTION_CAP. |
limits.gatewayDeposit.usd | The most one deposit into Circle Gateway may move. 50 when no policy sets it. Above it, the deposit is blocked with GATEWAY_DEPOSIT_ABOVE_CAP. |
limits.windows | Up to three budgets, each { "kind": "day", "usd": 25 }, where kind is hour, day or week. Over budget, the payment is blocked with BUDGET_EXCEEDED. |
thresholds | { "review": 50, "block": 70 }: the risk scores, from 1 to 100, at which a payment is held or blocked. By default 60 and 80. A policy can only lower them. |
schedule | When the agents of the policy may pay, described below. Outside it, the payment is held with OUTSIDE_SCHEDULE. |
rules | Up to 200 rules, described below. |
Budgets follow UTC: the current hour, the day from midnight UTC, the week from Monday. They count payments allowed in Protect mode, per agent. Each allowed payment reserves its amount before it is signed, so a burst of parallel payments cannot overshoot, and a payment that never settles gives its share back.
Payment schedule
A schedule keeps payments inside a window, like a pay run. Outside it, the payment is held for review with OUTSIDE_SCHEDULE, or blocked when outside is block.
{ "name": "Payroll agent", "scope": { "agentIds": ["payroll"] }, "schedule": { "timezone": "Europe/Paris", "days": [25, "last_business_day"], "hours": { "from": 9, "to": 18 }, "outside": "review" }}
| Field | What it does |
|---|---|
timezone | A timezone name, like Europe/Paris or America/New_York. Daylight saving time is followed. |
days | Days of the month, from 1 to 31, and "last_business_day": the last Monday to Friday of the month. Public holidays are not taken into account. |
weekdays | Days of the week, from 0 for Sunday to 6 for Saturday. |
hours | { "from": 9, "to": 18 }, in the timezone: from 9:00 included to 18:00 excluded. |
outside | review by default, or block. |
A schedule needs at least one of days, weekdays and hours. When it has several, a payment must fit all of them. A day a month does not have, like 31 in April, never matches: use last_business_day for the end of the month. Approving a held payment lets its retry through, as for any review.
Rules
A rule has an id, a condition if, an effect, and an optional reason shown with the decision, up to 256 characters.
| Effect | What it does |
|---|---|
review | Holds the payment for a human. |
block | Blocks it. |
alert | Lets it through, flagged in the dashboard. |
trust | Silences the reasons listed in suppress for the payments that match. Reasons that always block cannot be silenced. |
A condition is one of:
{ "field": "amountUsd", "op": "gt", "value": 1 }: compares a field of the payment.{ "signal": "NEW_DESTINATION" }: true when Warns raised that reason.{ "all": [...] },{ "any": [...] }or{ "not": {...} }: combines conditions, nested up to four levels.
Fields: agentId, environment, amountUsd, amountAtomic, asset.id, asset.network, scheme, payer.wallet, destination.address, request.origin (the site that asked for the payment), request.method, intent.purpose, intent.expectedMaxUsd, hourOfDayUtc, dayOfWeekUtc.
Operators: eq, neq, gt, gte, lt, lte, in and nin (up to 500 values), and matches, a pattern where * stands for anything and ? for one character. Addresses, origins and methods are compared without regard to case.
Examples
{ "id": "review-every-new-destination", "if": { "signal": "NEW_DESTINATION" }, "effect": "review", "reason": "New payout address"}
{ "id": "known-providers-only", "if": { "not": { "field": "request.origin", "op": "in", "value": ["https://api.vendor.com", "https://data.example.org"] } }, "effect": "block", "reason": "Not an approved provider"}
{ "id": "night-review", "if": { "all": [ { "field": "amountUsd", "op": "gt", "value": 1 }, { "field": "hourOfDayUtc", "op": "lt", "value": 6 } ] }, "effect": "review", "reason": "Large payment at night"}
{ "id": "vendor-new-address", "if": { "all": [ { "field": "request.origin", "op": "eq", "value": "https://api.vendor.com" }, { "field": "destination.address", "op": "eq", "value": "0x1111111111111111111111111111111111111111" } ] }, "effect": "trust", "suppress": ["DESTINATION_CHANGED", "NEW_DESTINATION"]}