On this page
FieldsAgents and accountsModesChecked again before a payment leavesApprove after a changeRead the policy with the APIWhat the content check readsReference / Policy
Policy
One policy per agent. It says where the agent may pay, whom, and how much. Edit it on the dashboard's Policy page, and a saved change applies to the next decision.
The proxy URL and the SDK hook enforce this policy on the payments routed through them. Every decision keeps a copy of the policy it ran under.
The MCP tool is advisory. The agent chooses whether to ask. For a check it cannot skip, use the proxy URL or the SDK hook.
Fields
Each field with its label on the dashboard and what a new agent starts with. Amounts are strings in USDC's smallest unit, so 100000 is 0.10 USDC.
| Field | Dashboard | Default | Code | Effect | 0 means | Limit |
|---|---|---|---|---|---|---|
perTxLimitAtomic | Per-transaction limit (USDC) | 0.10 USDC (100000) | amount_per_tx | A payment over this is denied. | 0 denies every payment. | Up to 12 digits before the point and 6 after. |
holdOverAtomic | Hold over (USDC) | Off (0) | amount_hold_over | A payment over this waits for a person, whatever the payee. | 0 turns the hold off. | Must be under the per-transaction limit, or the save is refused. |
dailyCapAtomic | Daily cap across all networks (USDC) | 1.00 USDC (1000000) | amount_daily_cap | One total in USDC for the agent per UTC day, on every network and through every lane. It counts allowed, approved and waiting payments, test USDC included. A payment that would go over it is denied. | 0 denies every payment. | Up to 12 digits before the point and 6 after. |
autoAllowFirstTimeUnderAtomic | Auto-allow unlisted payees under (USDC) | Off (0) | first_time_payee | A payment to a payee on neither list goes ahead when it is under this amount. Otherwise it waits for a person, every time, not only the first. | 0 sends every payment to an unlisted payee to review. | Up to 12 digits before the point and 6 after. |
velocityPerHour | Payments per hour per network | 20 | velocity_per_hour | Each network keeps its own count of allowed, approved and waiting payments in the last 60 minutes. Past it, the next payment on that network waits for a person. | 0 means every payment waits for a person. | A whole number from 0 to 999999. |
reviewTimeoutSeconds | Review timeout (seconds) | 120 (2 minutes) | None | How long a held payment waits for a person. Then it expires and nothing is paid. The reviews page covers the agent's own wait. | A whole number of seconds from 1 to 999999. | |
allowedResources | What the agent may buy | Empty, so any resource | resource_allowed | One https URL per line. Each covers itself and everything under it. A payment for anything else waits for a person. A contract call has no resource, so this list skips it. | 50 URLs of up to 500 characters, path only, with no query, fragment, username or password. | |
allowedNetworks | Networks and assets | Base Sepolia | network_allowed | A payment on a network you did not tick is denied in every mode. Saving Base or Solana Mainnet asks you to confirm first, since the agent then pays real USDC. | Base Sepolia, Base, Solana Devnet and Solana Mainnet. At least one. | |
allowedAssets | Networks and assets | Base Sepolia USDC | asset_allowed | Follows the networks. Each ticked network allows its own USDC and nothing else. A payment in another asset is denied in every mode. | ||
contractDenylist.selectors | Contract calls | None ticked | contract_call_denied | A ticked call is refused in enforce, whatever the amount. Refusing approve refuses increaseAllowance too. The guard supports only USDC transfer, transferFrom, and capped approve or increaseAllowance on Base and Base Sepolia. It refuses every other call in every mode, ticked or not. | ||
blocklistEnabled | Managed wallet blocklist | Off | wallet_blocklist | Screens each payee against address restrictions and USDC issuer freezes on Base and Solana. In enforce, a match, or a check that cannot finish, denies the payment. An allowlist entry on the same network skips it. No match means nothing was found in the lists checked, not that a wallet is cleared. | The switch stays off while the deployment has no current list. | |
allowedPayees | Allowlist | The demo seller | first_time_payee | A payment to a listed payee is not held for being unlisted. Add payees on Payees, or with Approve and always allow this payee on a review. Every agent on the account reads the same list. | 200 payees. | |
deniedPayees | Denylist | None | payee_denylisted | In enforce, every payment to a listed payee is denied, whatever the amount. A contract call counts as a payment to its spender or recipient, so this list blocks those too. Edit it on Payees. Every agent on the account reads the same list. | 200 payees. | |
contractDenylist.addresses | Not on the dashboard | None | contract_call_denied | Read only. It compares only the contract a call targets, not an approval's spender. To block a spender, add it to the payee denylist. |
Agents and accounts
- The limits belong to the agent. Two keys on one account never share a daily cap or an hourly count.
- An API key and the proxy token made with it are one agent, so they share one policy.
- Rotating a key on Keys keeps the agent, and today's spend still counts. A new key is a new agent with a policy of its own.
- The allowlist and the denylist belong to the account. Every agent on it reads the same two lists.
- The daily cap is one total across networks. With Base and Solana Mainnet ticked, a 1 USDC cap allows 1 USDC in all, not 1 USDC on each.
Modes
Mode belongs to the agent. Every agent starts in enforce. Switch it on the Mode card at the foot of Policy, or on Agents.
| Mode | Denies | Holds for review |
|---|---|---|
enforce | Every rule that denies. | Every rule that holds, until a person answers. |
observe | Only a wrong network, a wrong asset, a payee the seller's own 402 did not name, a declared payment method the guard cannot judge, or a contract call other than a USDC transfer, transferFrom, or capped approve or increaseAllowance on Base or Base Sepolia. | Nothing. The decision records what enforce would have done. |
In observe, shadowStatus on the decision holds what enforce would have said. The security model explains why those cases block in both modes.
Checked again before a payment leaves
A payment allowed or approved earlier is checked again when it comes back through the guard. That covers a proxy retry, an SDK wait or an advisory MCP wait on a review, and a repeat under the same Idempotency-Key. Five rules read the policy as it stands now.
- network (
network_allowed) - asset (
asset_allowed) - denylist (
payee_denylisted) - contract call (
contract_call_denied) - blocklist (
wallet_blocklist)
In observe, only the network and asset checks stop it. Lowering a limit does not stop a payment already allowed.
Approve after a change
Before an approval lands, the guard checks the network and the asset as the policy stands now. In enforce it also checks the payee denylist, and the managed wallet blocklist when that is on.
Approve and always allow this payee skips the blocklist, since an allowlisted payee is not screened. The daily cap stays as it was when the payment was held.
To pay a held payment under a cap you raised since:
Deny the held payment on Review, and confirm.
You see: the card moves under Resolved in the last day, stamped denied.
Run the payment again.
You see: a new decision judged under the new cap.
A payment held before midnight UTC cannot be approved after it. The reviews page has every button and check.
Read the policy with the API
The API key reads the policy behind it, payee lists included. It cannot change the policy.
curl -s https://vulsight-guard.vercel.app/api/v1/policy \
-H "authorization: Bearer $VULSIGHT_API_KEY"{
"allowedNetworks": ["eip155:84532"],
"allowedAssets": [
{
"network": "eip155:84532",
"address": "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
}
],
"allowedPayees": [
{
"network": "eip155:84532",
"address": "0x1111111111111111111111111111111111111111"
}
],
"deniedPayees": [],
"blocklistEnabled": false,
"allowedResources": [],
"perTxLimitAtomic": "100000",
"dailyCapAtomic": "1000000",
"holdOverAtomic": "0",
"velocityPerHour": 20,
"autoAllowFirstTimeUnderAtomic": "0",
"contractDenylist": {
"selectors": [],
"addresses": []
},
"reviewTimeoutSeconds": 120
}- Any method but GET answers 405 method_not_allowed.
- A missing key answers 401 missing_api_key, a mistyped or rotated one 401 unknown_api_key, and a revoked one 401 revoked_api_key.
What the content check reads
The content check is not a policy field. The deployment's administrators set it. It reads different text in each lane.
- The proxy URL: the seller's 402, pages fetched through it, and paid answers.
- The SDK: the pages the agent fetches with the SDK's fetch.
- The advisory MCP tool and the HTTP API: the text the caller sends.
The security model says what each result does to a payment.