> ## 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.

# Intents

> The approval-bearing primitive. No money moves without an approved intent.

An intent is a declaration of *what* an agent wants to pay for, *how much*, and *to whom*. The policy engine and risk engine evaluate every intent before it can settle. `requireIntent` is one of the [sacred invariants](/introduction#sacred-invariants) — never silently dropped, never bypassed.

## The intent model

| Field                 | Description                                                                    |
| --------------------- | ------------------------------------------------------------------------------ |
| `id`                  | `int_*`                                                                        |
| `agentId`             | Owning agent                                                                   |
| `cardId` / `cardName` | Which card to charge — exactly one of these                                    |
| `purpose`             | Free text — surfaces in audit and dashboard                                    |
| `expectedAmountEuros` | Optional but strongly recommended                                              |
| `expectedMerchant`    | Optional but strongly recommended                                              |
| `recurring`           | Optional — `cadence` (`daily`/`weekly`/`monthly`/`yearly`) + tolerance windows |
| `clientIntentId`      | Optional client-side dedupe key                                                |
| `ttlMinutes`          | Default 24h, max 30 days (`43200`)                                             |
| `status`              | FSM (below)                                                                    |

## Lifecycle (FSM)

```text theme={}
pending_approval → approved | denied | expired | cancelled
approved         → completed | failed | expired | cancelled
denied | completed | failed | expired | cancelled = terminal
```

| Status             | Meaning                                       |
| ------------------ | --------------------------------------------- |
| `pending_approval` | Policy or risk requires a human               |
| `approved`         | Auto-approved or human-approved; ready to pay |
| `completed`        | Successfully settled                          |
| `denied`           | Rejected by policy or risk                    |
| `failed`           | Settlement failed downstream                  |
| `expired`          | TTL passed                                    |
| `cancelled`        | Owner cancelled before settlement             |

## Create an intent

```bash theme={}
curl -X POST https://api.getovra.com/intents \
  -H "Authorization: Bearer $OVRA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "agentId": "ag_...",
    "cardName": "subscriptions",
    "purpose": "Renew Hetzner monthly",
    "expectedAmountEuros": 4.51,
    "expectedMerchant": "hetzner.com",
    "ttlMinutes": 60
  }'
```

Response:

```json theme={}
{
  "id": "int_...",
  "status": "approved",
  "expiresAt": "2026-04-20T11:00:00Z",
  "agentId": "ag_...",
  "cardId": "ca_...",
  "expectedAmountEuros": 4.51,
  "expectedMerchant": "hetzner.com"
}
```

If status is `pending_approval`, approve via dashboard or API.

## Approve / deny

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

# Deny
curl -X POST https://api.getovra.com/intents/int_.../deny \
  -H "Authorization: Bearer $OVRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Out of budget for the quarter" }'
```

## Recurring intents

A single intent can authorize a series of charges, with tolerance windows on amount and time:

```json theme={}
{
  "agentId": "ag_...",
  "cardName": "subscriptions",
  "purpose": "Hetzner — monthly server",
  "expectedAmountEuros": 4.51,
  "expectedMerchant": "hetzner.com",
  "recurring": {
    "cadence": "monthly",
    "windowStartDaysBefore": 3,
    "windowEndDaysAfter": 2,
    "amountTolerancePercent": 10,
    "merchantMatch": true
  }
}
```

## Verification (post-settlement)

After a charge completes you can record the actual amount and merchant for reconciliation:

```bash theme={}
curl -X POST https://api.getovra.com/intents/int_.../verify \
  -H "Authorization: Bearer $OVRA_API_KEY" \
  -d '{
    "actualAmountEuros": 4.51,
    "actualMerchant": "hetzner.com"
  }'
```

If `actualAmountEuros` deviates from `expectedAmountEuros` beyond the policy's `amountTolerancePercent`, the intent is flagged.

## Webhooks

| Event              | When                      |
| ------------------ | ------------------------- |
| `intent.created`   | Intent insert             |
| `intent.approved`  | Status flip to `approved` |
| `intent.denied`    | Policy or risk rejected   |
| `intent.expired`   | TTL hit                   |
| `intent.cancelled` | Owner cancelled           |

## Surfaces

| Surface   | Capability                                                      |
| --------- | --------------------------------------------------------------- |
| REST      | `/intents`, `/intents/:id/{approve,deny,verify,cancel}`         |
| SDK       | `ovra.intents.*`                                                |
| MCP       | `ovra_intent` (action: `declare` · `get` · `cancel` · `verify`) |
| Dashboard | `/dashboard/intents` (list, drawer, approve/deny inline)        |

## Next

<CardGroup cols={2}>
  <Card title="Policies" icon="shield-check" href="/concepts/policies">
    The rules that drive `approved` vs `pending_approval` vs `denied`.
  </Card>

  <Card title="Pay" icon="credit-card" href="/concepts/pay">
    What happens after `approved`.
  </Card>

  <Card title="Transactions" icon="receipt" href="/concepts/transactions">
    The record a settled intent produces.
  </Card>

  <Card title="Webhooks" icon="bell" href="/concepts/webhooks">
    React to FSM transitions in real time.
  </Card>
</CardGroup>
