Warns

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:

Starter policy
{  "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

FieldWhat it does
nameA label, up to 128 characters.
scope.agentIdsThe agents it applies to. Leave it out to apply the policy to every agent of the environment.
limits.perAction.usdThe most one payment may cost. Above it, the payment is blocked with AMOUNT_ABOVE_ACTION_CAP.
limits.gatewayDeposit.usdThe 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.windowsUp 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.
scheduleWhen the agents of the policy may pay, described below. Outside it, the payment is held with OUTSIDE_SCHEDULE.
rulesUp 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.

Pay run: the 25th and the last business day, 9:00 to 18:00 in Paris
{  "name": "Payroll agent",  "scope": { "agentIds": ["payroll"] },  "schedule": {    "timezone": "Europe/Paris",    "days": [25, "last_business_day"],    "hours": { "from": 9, "to": 18 },    "outside": "review"  }}
FieldWhat it does
timezoneA timezone name, like Europe/Paris or America/New_York. Daylight saving time is followed.
daysDays 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.
weekdaysDays 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.
outsidereview 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.

EffectWhat it does
reviewHolds the payment for a human.
blockBlocks it.
alertLets it through, flagged in the dashboard.
trustSilences 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

Hold every first payment to a new address
{  "id": "review-every-new-destination",  "if": { "signal": "NEW_DESTINATION" },  "effect": "review",  "reason": "New payout address"}
Only pay the providers you chose
{  "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"}
Hold large payments at night
{  "id": "night-review",  "if": {    "all": [      { "field": "amountUsd", "op": "gt", "value": 1 },      { "field": "hourOfDayUtc", "op": "lt", "value": 6 }    ]  },  "effect": "review",  "reason": "Large payment at night"}
Accept a merchant's new address once you checked it
{  "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"]}