# Spending Policies

Spending policies are the core governance primitive in TherosAI. Every agent vault is bound to a policy at creation. Policies define what an agent can spend, how fast, and with whom — and they are enforced **at the on-chain program level**, not in application code.

This distinction is foundational. Application-layer checks can be bypassed by a compromised agent, a misconfigured SDK, or a bug in your orchestration layer. On-chain enforcement means a transaction that violates policy **cannot execute** — the program rejects the instruction before it reaches the USDC SPL token program. There is no bypass path. No application-layer override. No appeal.

Rules enforced on-chain are rules that hold.

***

## Policy parameters

| Parameter           | Type               | Description                                                                | Example                  |
| ------------------- | ------------------ | -------------------------------------------------------------------------- | ------------------------ |
| `maxPerTx`          | string (USDC)      | Maximum USDC per single transaction                                        | `"5.00"`                 |
| `maxPerDay`         | string (USDC)      | Rolling 24-hour spend ceiling                                              | `"50.00"`                |
| `maxPerMonth`       | string (USDC)      | Calendar-month budget                                                      | `"500.00"`               |
| `allowedRecipients` | string\[]          | Explicit recipient allowlist. If set, only these recipients are permitted. | `["api.perplexity.ai"]`  |
| `blockedCategories` | string\[]          | Category codes to block                                                    | `["EXCH", "GAMB"]`       |
| `velocityCap`       | integer            | Max transactions per hour                                                  | `20`                     |
| `expiry`            | ISO 8601 timestamp | Policy expiry — vault freezes after this time                              | `"2026-06-01T00:00:00Z"` |
| `requireCoSign`     | string (USDC)      | Transactions at or above this amount require operator MPC co-signature     | `"100.00"`               |

All monetary values are strings to avoid floating-point precision issues.

***

## Creating a policy

```typescript
const policy = await client.policies.create({
  name: "Conservative — Research Agent",
  maxPerTx: "5.00",
  maxPerDay: "50.00",
  maxPerMonth: "500.00",
  velocityCap: 20,
  allowedRecipients: [
    "api.perplexity.ai",
    "api.tavily.com",
    "api.serper.dev",
  ],
});
```

Policies are reusable — create one policy and attach it to many vaults. When you update a policy, changes propagate to all vaults using it within one Solana slot (\~400ms).

***

## Recipient allowlisting

The `allowedRecipients` field accepts Solana wallet addresses and domain strings. Domain matching is exact — `api.perplexity.ai` does not match `perplexity.ai` or `sub.api.perplexity.ai`.

If `allowedRecipients` is empty or omitted, the vault can pay any recipient not in `blockedCategories`. If `allowedRecipients` is set, it acts as an explicit allowlist — any payment to an unlisted recipient is blocked.

***

## Blocked categories

TherosAI maintains a category taxonomy for known on-chain recipients. Blocking a category prevents payments to any recipient classified under it:

| Code     | Category                                  |
| -------- | ----------------------------------------- |
| `EXCH`   | Centralised and decentralised exchanges   |
| `GAMB`   | Gambling and prediction markets           |
| `BRIDGE` | Cross-chain bridge protocols              |
| `NFT`    | NFT marketplaces                          |
| `DEFI`   | DeFi protocols (lending, liquidity pools) |

Category classification is maintained by TherosAI and updated as new protocols are identified.

***

## Velocity cap

The `velocityCap` is a rolling per-hour transaction count limit, independent of spend limits. An agent can be capped at 20 transactions per hour even if those transactions are individually small and within day and month limits.

This is the primary guard against runaway agent loops that generate many small payments in a short window.

***

## MPC co-sign

When `requireCoSign` is set, any transaction at or above that threshold requires a 2-of-2 signature before execution:

1. The agent's session authority token (required for all transactions)
2. An operator co-signature — generated via the dashboard approval flow or a programmatic signing endpoint

This creates a human-in-the-loop checkpoint for large movements without adding latency to normal agent operations below the threshold.

→ See [Security Model](/core-concepts/security-model.md) for details on the MPC architecture.

***

## Updating a policy

```typescript
await client.policies.update("pol_conservative", {
  maxPerDay: "75.00",
  velocityCap: 30,
});
```

Changes take effect within one Solana slot. The policy audit log records every change — who made it, when, and what changed. This log is accessible in the dashboard under **Policies → \[policy name] → Audit Log**.

***

## Policy enforcement behaviour

When a payment is submitted:

1. The TherosAI on-chain program checks all applicable policy rules.
2. If any rule is violated, the transaction instruction is **rejected** — it never reaches the USDC token program.
3. A `transaction.blocked` event is emitted with the specific rule that was violated.
4. The vault's status is updated to `policy_violation` in the dashboard.

```json
{
  "event": "transaction.blocked",
  "wallet_id": "wal_9f3a2b",
  "attempted_amount": "12.00",
  "recipient": "api.perplexity.ai",
  "violation": {
    "rule": "max_per_tx",
    "limit": "5.00",
    "attempted": "12.00"
  },
  "timestamp": "2026-04-29T12:00:00Z"
}
```

Policy violations do not automatically freeze the vault. Operators can configure alerts on violation, but the vault continues operating — subsequent transactions that comply with policy will succeed.

***

## Preset policies

TherosAI provides preset policies for common use cases. These can be used as-is or cloned as a starting point:

| Preset ID          | Description                     | `maxPerTx` | `maxPerDay` | `velocityCap` |
| ------------------ | ------------------------------- | ---------- | ----------- | ------------- |
| `pol_conservative` | Low-spend research agent        | `$5`       | `$50`       | 20/hr         |
| `pol_moderate`     | Standard API-consuming agent    | `$25`      | `$250`      | 50/hr         |
| `pol_permissive`   | High-throughput orchestrator    | `$100`     | `$1,000`    | 200/hr        |
| `pol_locked`       | Funded but frozen — no spending | `$0`       | `$0`        | 0/hr          |

Preset policies cannot be modified. Clone a preset to create a customised variant:

```typescript
const policy = await client.policies.clone("pol_conservative", {
  name: "Conservative + Perplexity only",
  allowedRecipients: ["api.perplexity.ai"],
});
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.therosai.com/core-concepts/spending-policies.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
