Action Integrity
Action Integrity concepts
How an action is checked, decided, executed, verified and recorded: decision vs outcome, idempotency, the Unknown state and evidence classes.
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.
integrity.execute().Action Gateway (MCP)Give an agent its actions as MCP tools.Policies and approvalsRules in plain language, run as deterministic code.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.
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 |
|---|---|---|
| Identity | The agent key’s agent; an optional agent claim must match it. | Denied |
| Permission | The agent is active, the action is in its allowed_actions, and an execute-mode connector supports it. | Denied |
| Business rules | Built-in rules per action type on fresh provider data (a refund ≤ captured − already refunded − refunds in flight; not disputed; currency; subscription state), plus your policy deny rules. Without provider context the check fails closed. | Denied (monitoring: would deny) |
| Financial limits | Your limit rules: per-action cap, rolling budget, velocity; per agent, customer, resource or organization. | Denied or approval |
| Duplicates | The same action type on the same resource or order, for the same amount, within 72 hours: done, in flight or waiting. | Denied (monitoring: would deny) |
| Approval | A require_approval rule matches. | Pending approval |
Decision and outcome
Two separate fields, so an action is never “approved but failed” in one status.
- Decision:
allowed,denied,pending_approval, orwould_deny(monitoring mode). - Outcome:
not_executed,awaiting_approval,executing,completed(verified by re-reading the provider object),failed, orunknown.
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 getsmetadata[audon_action_id]), 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, 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: soft denials are recorded as would_deny and the action still runs, and approvals aren’t waited for. A person switches the organization to enforcing, or sets a single agent to enforce. Hard denials (a paused agent, an action it isn’t allowed, no connector, no provider context) always deny.
Agents and agent keys
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 Loss Prevention shows next to its actions.
- Agent keys (
aa_agent_…) are bound to one agent and carry onlyintegrity: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 Loss Prevention: 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.