> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getovra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Control

> Spending policies and guardrails that enforce themselves.

Control is the rules layer. Two engines — **Policy** and **Risk** — co-evaluate every intent, grant, issue, redeem, and checkout. Decisions are server-side; agents cannot bypass their own rules.

## The two engines

<CardGroup cols={2}>
  <Card title="Policy Engine" icon="shield-check">
    Pure function `evaluatePolicy(policy, input) → { allowed, requiresApproval, reason }`. Deterministic, declarative, plan-tier-agnostic.
  </Card>

  <Card title="Risk Engine" icon="radar">
    Stateful. 5 signals (velocity, amount-anomaly, geo, session, time-anomaly). Score 0–100 with tunable thresholds for review/deny/freeze.
  </Card>
</CardGroup>

## Policy: what can be expressed

Policies are JSON-serializable objects attached to an agent (one policy per agent at a time). The full schema lives in `/concepts/policies` — here's the surface:

| Group                 | Fields                                                                                        |
| --------------------- | --------------------------------------------------------------------------------------------- |
| **Limits**            | `maxTransactionEuros`, `dailyLimitEuros`, `weeklyLimitEuros`, `monthlyLimitEuros`             |
| **Approval**          | `autoApproveLimitEuros`, `enforcementLevel` (`enforce` / `approve` / `warn`)                  |
| **Merchant**          | `merchantAllowlist`, `merchantBlocklist`, `allowedMccs`, `blockedMccs`, `lockToFirstMerchant` |
| **Geo**               | `allowedCountries`, `blockedCountries` (ISO 3166-1 alpha-2)                                   |
| **Time**              | `activeHoursStart`, `activeHoursEnd`, `timezone`, `allowedDays`, `cooldownMinutes`            |
| **Lifecycle**         | `expiresAfterMinutes`, `expirationAction` (`freeze` / `close` / `notify`), `maxUsageCount`    |
| **Required behavior** | `requireIntent` (sacred — never silently dropped), `requireAttestation`                       |

## Risk: signals and thresholds

| Signal           | What it checks                                       | Default weight |
| ---------------- | ---------------------------------------------------- | -------------- |
| `velocity`       | N transactions in M minutes vs baseline              | High           |
| `amount-anomaly` | Z-score against historical spend mean/stddev         | High           |
| `geo`            | New country vs policy + history; "impossible travel" | Medium         |
| `session`        | User-Agent / IP cluster anomalies                    | Medium         |
| `time-anomaly`   | Outside business hours or stated active windows      | Low            |

Score is a weighted aggregate. Thresholds are tunable per policy (5min cache):

| Threshold         | Default | Action                                 |
| ----------------- | ------- | -------------------------------------- |
| `thresholdReview` | 70      | Hold for human approval                |
| `thresholdDeny`   | 85      | Reject the intent                      |
| `thresholdFreeze` | 95      | Freeze the agent (opt-in, default off) |

<Warning>
  Auto-freeze is opt-in. It will halt legitimate flows if your tolerance isn't calibrated — recommend turning it on only after observing risk scores in production.
</Warning>

## Where Control runs

```text theme={}
intent → grant → issue → redeem → checkout
   ↑       ↑       ↑       ↑          ↑
  policy + risk evaluated at each checkpoint
```

Each evaluation writes a row to `decision_logs` with the full signal payload, the score, the reason, and the action taken. Queryable via `/audit` and `/risk/violations`.

## Card-controls sync

When you change a policy, the system translates the relevant fields into card-issuer-native controls (MCC rules, country rules, per-tx limits) and pushes them to the underlying card. Policy is the source of truth; the card mirror is best-effort and is reconciled on every update.

## Approval workflow

When `enforcementLevel: "approve"` or risk action is `review`, the intent enters `pending_approval`. Approve via:

```bash theme={}
curl -X POST https://api.getovra.com/intents/int_.../approve \
  -H "Authorization: Bearer $OVRA_API_KEY"
```

Or `POST /intents/:id/deny` with an optional `reason`.

## Quick example — read an agent's policy

```bash theme={}
curl https://api.getovra.com/policies?agentId=ag_... \
  -H "Authorization: Bearer $OVRA_API_KEY"
```

Or via MCP — agents can **read** their policy but never modify it:

```text theme={}
ovra_policy { action: "get", agentId: "ag_..." }
```

## Pre-built presets

`policy-presets.ts` ships templates for common patterns: travel-only, subscriptions, dev-tools, etc. Use them as starting points in the dashboard create flow.

## Surfaces

| Surface   | Capability                                                               |
| --------- | ------------------------------------------------------------------------ |
| REST      | `GET/POST/PATCH /policies`, `GET /risk/violations`, `PATCH /risk/config` |
| SDK       | `policies.*`                                                             |
| MCP       | `ovra_policy` (read-only by design)                                      |
| Dashboard | `/dashboard/policies`, `/dashboard/risk`, `/dashboard/agents/:id`        |

## Next

<CardGroup cols={2}>
  <Card title="Policies" icon="shield-check" href="/concepts/policies">
    Full field reference with examples for each rule.
  </Card>

  <Card title="Intents" icon="file-signature" href="/concepts/intents">
    The approval-bearing primitive Control gates.
  </Card>

  <Card title="Intelligence" icon="chart-line" href="/concepts/intelligence">
    What Control writes — decision logs, audit, anomaly detection.
  </Card>

  <Card title="Disputes" icon="gavel" href="/concepts/disputes">
    What happens when something Control allowed turns out to be wrong.
  </Card>
</CardGroup>
