Skip to main content

Failures, Cost, and Audit

ANIP does not stop at "success or error." It makes failures actionable, costs predictable, and execution verifiable.

Structured failures

When an ANIP invocation fails, the response includes structured information the agent can act on — not just an HTTP status code:

{
"success": false,
"invocation_id": "inv_8b2f4a",
"failure": {
"type": "budget_exceeded",
"detail": "Requested booking costs $487.00 which exceeds the delegated budget of $200.00",
"retry": false,
"resolution": {
"action": "request_budget_increase",
"requires": "higher_budget_delegation",
"grantable_by": "human:[email protected]",
"estimated_availability": "immediate"
}
}
}

Failure fields

FieldPurpose
failure.typeMachine-readable failure category
failure.detailHuman-readable explanation
failure.retryWhether retrying the same call might succeed
failure.resolution.actionWhat to do to resolve the failure
failure.resolution.requiresWhat's needed (scope, budget, approval)
failure.resolution.grantable_byWho can grant what's needed
failure.resolution.estimated_availabilityHow soon resolution is possible

Why this matters

Compare these two failure experiences:

REST API: Agent gets 403 Forbidden. What does that mean? Missing auth? Wrong scope? Expired token? Rate limited? The agent has to guess.

ANIP: Agent gets a structured failure that says "you need a higher budget, your manager can grant it, and it's available immediately." The agent can report this to the user and take specific action to resolve the block.

v0.15 failure types

v0.15 adds non_delegable_action for capabilities that require the direct (root) principal:

Failure typeDescription
non_delegable_actionThe capability requires the direct principal (root of the delegation chain) and cannot be invoked by a delegated agent. The human must invoke this capability directly.

Example:

{
"success": false,
"invocation_id": "inv-9c1e2f3a4b5d",
"failure": {
"type": "non_delegable_action",
"detail": "destroy_environment requires direct principal action and cannot be delegated",
"retry": false,
"resolution": {
"action": "invoke_as_root_principal",
"requires": "direct_principal_invocation"
}
}
}

v0.16 recovery_class — coarse recovery strategy

v0.16 adds recovery_class to every resolution object. It is a coarser classification of the recovery strategy implied by resolution.action, enabling agents to route failures without pattern-matching on individual action strings.

Advisory: recovery_class is advisory. It does not override or replace the retry boolean. retry retains its meaning — whether the same request could succeed after the resolution is applied. recovery_class conveys how to recover, not whether to retry.

recovery_class vocabulary:

recovery_classMeaning
retry_nowRetry immediately — no external change required.
wait_then_retryWait for a time-bounded condition (e.g. rate-limit window, service cooldown), then retry.
refresh_then_retryRefresh a local artifact (binding, quote, token) and retry.
redelegation_then_retryObtain a new or modified delegation token from an authority, then retry.
revalidate_then_retryRe-fetch and validate service-side state (manifest, capability graph) before retrying.
terminalNo automated recovery path — requires human escalation or service-owner intervention.

terminal invariant: a failure with recovery_class: "terminal" MUST have retry: false. All other classes are compatible with either retry value.

Example failure response with recovery_class:

{
"success": false,
"invocation_id": "inv-9c1e2f3a4b5d",
"failure": {
"type": "insufficient_scope",
"detail": "Token lacks travel.book scope required by book_flight",
"retry": false,
"resolution": {
"action": "request_broader_scope",
"requires": "travel.book",
"grantable_by": "human:[email protected]",
"recovery_class": "redelegation_then_retry"
}
}
}

Canonical authority resolution actions (v0.15)

The resolution.action field uses canonical string values. The table below lists all authority-related resolution actions, including five new ones added in v0.16:

resolution.actionrecovery_classMeaning
request_broader_scoperedelegation_then_retryObtain a delegation token with wider scope
request_budget_increaseredelegation_then_retryObtain a higher-budget delegation
invoke_as_root_principalterminalThe human must invoke directly (non-delegable)
obtain_bindingrefresh_then_retryInvoke the source capability first to get a binding
refresh_bindingrefresh_then_retryRe-invoke the source capability for a fresh quote
obtain_quote_firstrefresh_then_retryGet a bound price before invoking an estimated-cost capability
obtain_matching_currencyredelegation_then_retryRe-delegate with matching budget currency
retry_nowretry_nowRetry immediately — transient condition resolved
provide_credentialsretry_nowProvide or refresh credentials and retry
wait_and_retrywait_then_retryWait for a time-bounded window, then retry
revalidate_staterevalidate_then_retryRe-fetch service-side state before retrying
check_manifestrevalidate_then_retryRe-fetch the manifest and verify capability declarations
request_budget_bound_delegationredelegation_then_retryRequest a delegation with an explicit budget bound
request_matching_currency_delegationredelegation_then_retryRe-delegate with a currency that matches the capability cost
request_new_delegationredelegation_then_retryObtain a fresh delegation token (existing token expired or revoked)
request_capability_bindingredelegation_then_retryObtain a delegation that binds to a specific capability
request_deeper_delegationredelegation_then_retryObtain a delegation from a principal higher in the chain
escalate_to_root_principalterminalEscalate to the root principal; no automated recovery
contact_service_ownerterminalThe service owner must intervene; no automated recovery

Deprecation: The request_scope_grant value for resolution.action was removed in v0.15. All conformant implementations must use request_broader_scope instead. Clients that check for request_scope_grant should be updated.

v0.14 failure types

v0.14 adds six failure types for budget, binding, and control scenarios:

Failure typeDescription
budget_exceededThe invocation cost exceeds the token's budget constraint. When cost certainty is exact, the check compares the exact amount; when estimated, the check uses range_max.
budget_currency_mismatchThe token's budget currency does not match the capability's cost currency.
budget_not_enforceableThe capability declares cost but the token lacks a budget constraint required by the service's control requirements.
binding_missingThe capability has requires_binding: true but no binding reference was provided in the invocation request.
binding_staleA binding reference was provided but has expired or is no longer valid.
control_requirement_unsatisfiedA control_requirements entry (e.g. cost_ceiling, stronger_delegation_required) was not met.

Budget context

v0.14 invoke responses include a budget_context object on both success and failure, giving agents visibility into budget consumption:

{
"success": true,
"result": { "booking_id": "BK-7291" },
"cost_actual": { "currency": "USD", "amount": 487.00 },
"budget_context": {
"budget_currency": "USD",
"budget_max": 500.00,
"budget_consumed": 487.00,
"budget_remaining": 13.00
}
}

On failure, budget_context shows why the budget check failed:

{
"success": false,
"failure": {
"type": "budget_exceeded",
"detail": "Estimated max cost $800.00 exceeds budget of $500.00"
},
"budget_context": {
"budget_currency": "USD",
"budget_max": 500.00,
"check_amount": 800.00,
"check_basis": "range_max"
}
}

The check_basis field indicates how the enforcement amount was determined: exact when cost certainty is exact, or range_max when cost certainty is estimated (the service uses the worst-case amount to protect the budget).

Cost signaling

ANIP lets services declare cost expectations before invocation and return actual costs after:

Before invoke (from manifest)

{
"cost": {
"certainty": "estimated",
"financial": {
"currency": "USD",
"range_min": 200,
"range_max": 800,
"typical": 420
}
}
}

After invoke (in response)

{
"success": true,
"cost_actual": { "currency": "USD", "amount": 487.00 },
"result": { "booking_id": "BK-7291" }
}

This lets agents:

  • Compare alternatives before committing (search multiple options, pick the cheapest)
  • Stay within budget constraints from their delegation chain
  • Report cost to the user before and after execution

Audit logging

Every ANIP invocation is automatically logged with:

  • Capability name and invocation ID
  • Caller identity (from the delegation chain)
  • Event classification (low_risk_success, high_risk_success, low_risk_failure, etc.)
  • Timestamp
  • Retention policy compliance

Querying audit

curl -X POST https://service.example/anip/audit \
-H "Authorization: Bearer <bootstrap-token>" \
-H "Content-Type: application/json" \
-d '{"capability": "book_flight", "limit": 5}'
{
"entries": [
{
"invocation_id": "inv_8b2f4a",
"capability": "book_flight",
"actor_key": "human:[email protected]",
"event_class": "high_risk_failure",
"success": false,
"timestamp": "2026-03-27T10:30:00Z"
}
]
}

Security features

ANIP audit includes several hardening features:

  • Event classification: Separates low-risk (reads) from high-risk (writes, financial) events for different retention and alerting
  • Retention policy: Services declare how long audit entries are kept, with separate policies for different risk levels
  • Failure redaction: Sensitive details can be redacted from audit entries based on caller class
  • Aggregation: High-volume read operations can be aggregated to manage storage without losing audit coverage

Next steps