---
title: "Action Control concepts: checks and evidence · Audon"
description: "How an AI agent action is checked, decided, executed, verified and recorded: decision vs outcome, idempotency, the Unknown state and evidence classes."
url: https://audon.ai/docs/action-control
last_updated: 2026-11-01
---
Action Control

# Action Control concepts

How an action is checked, decided, executed, verified and recorded: decision vs outcome, idempotency, the Unknown state and evidence classes.

Agent Action Control (formerly Agent Action Integrity) is a control layer between your AI agents and the systems where money moves. Every business action an agent proposes (a refund, a customer credit, a discount, a subscription change, an order cancel) is checked against your rules before it runs, executed through an Audon connector, its real outcome is verified at the provider, and the evidence is kept. Audon controls the actions, not the model, so it works with any model, agent or framework.

[Run the free action audit Read-only: Stripe or Shopify history and agent logs, up to 90 days.](https://audon.ai/docs/action-control/audit) [Quickstart: SDK Protect a refund with `integrity.execute()`.](https://audon.ai/docs/action-control/quickstart) [Action Gateway (MCP) Give an agent its actions as MCP tools.](https://audon.ai/docs/action-control/gateway) [Policies and approvals Rules in plain language, run as deterministic code.](https://audon.ai/docs/action-control/policies)

## The pipeline

One call, `integrity.execute`, runs the whole path. Authorization and execution are coupled: the checks and the reservation of the action run in one transaction under locks on the idempotency key and the resource, and the provider context is read under the same lock. A second request on the same order waits and then sees the first one in flight.

integrity.execute

```
identity → permission → business rules (fresh provider context)
  → financial limits → duplicates → approvals → decision
  → execute via connector → verify (re-read) → evidence
```

| Check | What it checks | On failure (enforcing) | Monitoring |
| --- | --- | --- | --- |
| Identity | The agent key’s agent; an optional `agent` claim must match it. | Denied | Denied (built-in) |
| Permission | The agent is active, the action is in its `allowed_actions`, and an execute-mode connector supports it. | Denied | Denied (built-in) |
| Business rules | Built-in rules per action type on fresh provider data (a refund ≤ captured − already refunded − refunds in flight; not disputed; not already cancelled; currency; subscription state). Without provider context the check fails closed. | Denied | Denied (built-in) |
| Your policy | Your `deny` rules and a `default_effect: deny`. | Denied | Would deny (shadow) |
| Financial limits | Your `limit` rules: per-action cap, rolling budget, velocity; per agent, customer, resource or organization. | Denied or approval | Would deny (shadow) |
| Duplicates | The same action type on the same resource or order, for the same amount, within 72 hours: done, in flight or waiting. | Denied | Denied (built-in) |
| Approval | A `require_approval` rule matches. | Pending approval | Would need approval (shadow) |

## Decision and outcome

Two separate fields, so an action is never “approved but failed” in one status.

- **Decision**: `allowed`, `denied`, `pending_approval`, or `would_deny` (monitoring mode: one of your own policies would have denied it; the action ran).
- **Outcome**: `not_executed`, `awaiting_approval`, `executing`, `completed` (verified by re-reading the provider object), `failed`, or `unknown`.

A denial is a result with a code and a reason (for example `already_fully_refunded`, “Order already fully refunded.”), not an error. Relay it to the person; don’t try another way to do the same thing.

## Idempotency

Every execute call needs an idempotency key (8–200 characters), as an `Idempotency-Key` header or `idempotency_key`. Use one key per intent and reuse it on every retry of that intent.

- The same key returns the original action’s current state with `replayed: true`. It never executes again.
- The same key with a different body is `idempotency_conflict`.
- Each attempt is sent to the provider with its own key, `audon_<action id>_<attempt>` (Stripe also gets `metadata[audon_action_id]`; Shopify refunds use `@idempotent` and a note marker), so Audon can find it there later.

## The Unknown state

When the provider’s answer doesn’t say what happened, the outcome is `unknown`. Audon never retries it blindly: it asks the provider first.

| Provider answer | Outcome | Retry |
| --- | --- | --- |
| 2xx | `completed` after a re-read, or `executing` while pending | — |
| 4xx (including 429), or refused before sending (DNS, connection refused) | `failed`, `retry_safe: true` | Allowed |
| Timeout or reset after sending, 409, 5xx, or a webhook 2xx without a status | `unknown` | Not until resolved |

A reconcile job looks the action up at the provider, first after 30 seconds and then backing off up to 24 hours (12 attempts): Stripe refunds by charge and `audon_action_id`, credits by balance-transaction metadata, discounts by their deterministic coupon id, subscription changes by the subscription’s state; Shopify refunds by the note on the order’s refunds, cancellations by `cancelledAt`, discount and gift card codes by their deterministic code; and Webhook connectors through your status endpoint. Found means `completed`. Reported absent twice, at least a minute apart, means `failed` with `retry_safe: true`. Anything else stays `unknown` and counts as exposure. A retry re-checks business rules and duplicates under the lock and sends the next attempt with a new provider key.

## Monitoring and enforcing

An organization starts in **monitoring** with its first live action. The mode only applies to **your own policies** (limits, deny rules, default deny, approvals): in monitoring they run in shadow, recorded as `would_deny` (or an allowed action with `decision_code: would_require_approval`), and the action still runs. A person switches the organization to **enforcing**, or sets a single agent to `enforce`.

**Built-in safety checks enforce in every mode**, from the first action: the agent key and claimed agent, a paused agent (kill switch), an action the agent isn’t allowed, no execute connector or provider context, the built-in business rules (a refund above the refundable remainder; a refund on a fully refunded or disputed payment; cancelling an order or subscription that is already cancelled), duplicates, and the Unknown state (nothing is retried until reconciliation proves the outcome). A blocked duplicate in monitoring is a normal `denied` decision with the usual evidence class. The checks record which of them were enforcing and which ran in shadow (`basis` and `enforcement` on each check, `decision_basis` in the evidence).

## Agents and agent keys

**An agent in Audon is a record for an AI agent you already run**: your support bot, a sales agent, a coding agent. Audon doesn’t build or run agents. Registering one under [Agents](https://audon.ai/app/agents) gives it an identity and its own key, so Audon can:

1. **Check its business actions** (refunds, credits, discounts, cancellations) against your rules before they run. This is Agent Action Control.
2. **Attribute AI spend to it**: map the provider projects, workspaces or API keys it uses, and its model spend from Agent Spend Optimization shows on its Cost tab. No agent key needed for this.
3. **Pause it instantly**: one click denies every action it proposes until a person resumes it.

### How an agent connects

1. Create its agent key on the agent’s Keys tab (shown once).
2. Add the key to your agent: as MCP tools through the [Action Gateway](https://audon.ai/docs/action-control/gateway), or with the [SDK](https://audon.ai/docs/action-control/quickstart) or one HTTP call. The Keys tab has a ready snippet for each client.
3. Send a test action. It runs through the Test connector and moves no money.

The Agents list shows each agent’s connection: **Not connected yet** (no call with its key so far), **Connected** with when Audon last saw it, **Spend only** (its spend is mapped but it hasn’t sent an action) or **Paused**. An agent that isn’t connected sends nothing to Audon, so nothing is checked: its actions go straight to your systems until you route them through Audon.

Under the hood, an agent is a platform object (`agt_…`) with a name, an owner, an environment, its allowed actions and a mode. It can carry cost attribution (provider project, workspace or API key ids), so its spend from Agent Spend Optimization shows next to its actions.

- **Agent keys** (`aa_agent_…`) are bound to one agent and carry only `integrity:execute`. They stop working when revoked, when they expire or when their creator leaves the organization.
- **No role holds `integrity:execute`**: people, admin API keys and OAuth grants can never execute an action. Agent keys can never approve, change policies or read other agents’ evidence.
- **Pause agent** is the kill switch: every action the agent proposes is denied until a person resumes it.

## Evidence classes

The same grammar as Agent Spend Optimization: potential and confirmed are never added together, and only confirmed is green.

| Class | Meaning |
| --- | --- |
| `observed_action` | The action was recorded with its decision and outcome. |
| `flagged_action` | An audit finding or anomaly, such as a possible duplicate refund. |
| `potentially_prevented_loss` | A blocked or held attempt, at its amount at risk. Retries of the same intent count once: five blocked retries of a $450 refund are $450. |
| `confirmed_prevented_loss` | Proof the action would otherwise have executed and caused loss: for a duplicate, the connector confirms the earlier action as completed and the provider would have accepted a second one (for example a customer balance credit). A reviewer can also confirm it, with a reason, and is named on the record. |
| `outcome_unknown` | The provider’s state is not known yet: exposure, neither loss nor benefit. |
| `suspected_loss` , `confirmed_loss` | Historical audit only: losses that already happened. Never called prevented. |

Blocked is not the same as prevented

A blocked attempt alone is no proven financial benefit. In the $450 example, the order was already refunded in full and Stripe would have refused a second refund too, so the block is potentially prevented loss, not confirmed.

## Evidence

Each action keeps an append-only list of evidence records (proposed, each check, the decision, the execution attempts, the verified outcome), hash-chained over canonical JSON; the database refuses to change them. Every record references the active policy versions and the rules that matched. Admin changes go to the activity log, cross-linked by action id.

## Security model: route every action through Audon

The controls hold for the actions that pass through them. Give the agent an agent key and the Action Gateway or the SDK, and remove its direct access to the same APIs (for example a Stripe secret key in its environment). Otherwise the agent can bypass the controls. Setup, the gateway and every connector response repeat this.

Money is always integer minor units with a currency (`{ amount_minor: "45000", amount_decimal: "450.00", currency: "USD" }`), never summed across currencies. Plans: see [pricing](https://audon.ai/pricing#action-control).

Last updated 1 November 2026
