Skip to main content

Capability Declaration

ANIP is capability-first, not endpoint-first. Instead of documenting HTTP routes and hoping agents figure out what they do, ANIP services declare capabilities with their full execution context — what they do, what they cost, what authority they require, and what side effects they produce.

What a capability declares

Every capability in the manifest includes:

FieldPurposeExample
nameUnique identifier"search_flights"
descriptionWhat it does (for agents and humans)"Search available flights"
inputsRequired and optional parameters[{ name: "origin", type: "airport_code" }]
outputReturn shape{ type: "flight_list", fields: ["number", "price"] }
side_effectRead, write, transactional, or irreversible{ type: "read" }
minimum_scopeRequired delegation scope["travel.search"]
costFinancial and compute cost hints{ certainty: "estimated", financial: { currency: "USD", range_min: 200, range_max: 800 } }
contract_versionCapability contract version"1.0"

Full manifest example

The manifest is served at GET /anip/manifest with a cryptographic signature in the X-ANIP-Signature header:

{
"manifest_metadata": {
"version": "0.23.0",
"sha256": "a1b2c3...",
"issued_at": "2026-03-27T10:00:00Z"
},
"service_identity": {
"id": "travel-service",
"jwks_uri": "/.well-known/jwks.json",
"issuer_mode": "self"
},
"trust": {
"level": "signed",
"anchoring": { "cadence": "hourly" }
},
"capabilities": {
"search_flights": {
"description": "Search available flights between airports",
"contract_version": "1.0",
"inputs": [
{ "name": "origin", "type": "airport_code", "required": true, "description": "Departure airport" },
{ "name": "destination", "type": "airport_code", "required": true, "description": "Arrival airport" },
{ "name": "date", "type": "date", "required": false, "description": "Travel date" }
],
"output": { "type": "flight_list", "fields": ["flight_number", "origin", "destination", "price"] },
"side_effect": { "type": "read" },
"minimum_scope": ["travel.search"],
"cost": { "certainty": "fixed" }
},
"book_flight": {
"description": "Book a flight reservation",
"contract_version": "1.0",
"inputs": [
{ "name": "flight_number", "type": "string", "required": true },
{ "name": "passengers", "type": "integer", "required": true, "default": 1 }
],
"output": { "type": "booking_confirmation", "fields": ["booking_id", "status"] },
"side_effect": { "type": "irreversible" },
"minimum_scope": ["travel.book"],
"cost": {
"certainty": "estimated",
"financial": { "currency": "USD", "range_min": 200, "range_max": 800, "typical": 420 }
}
}
}
}

Side-effect types

Side-effect declaration is one of ANIP's most important features. It tells the agent what kind of change a capability produces before it acts:

TypeMeaningAgent behavior
readNo state changeSafe to call speculatively
writeProduces reversible state changeAgent should confirm intent
transactionalState change with rollback windowAgent can undo within time limit
irreversiblePermanent state changeAgent must have explicit authorization

This is fundamentally different from REST, where an agent must infer from HTTP methods (GET/POST/PUT/DELETE) what side effects might occur — and those conventions aren't enforced.

Cost declaration

Capabilities can declare expected costs before invocation:

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

After invocation, the actual cost is returned in the response:

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

This lets agents compare alternatives and stay within budget constraints before committing to an action.

Capability graph

Capabilities can declare prerequisites and compensation paths:

{
"book_flight": {
"requires": [
{ "capability": "check_availability", "reason": "must verify seat availability" }
],
"side_effect": {
"type": "transactional",
"rollback_window": "24h",
"compensation": "cancel_booking"
}
}
}

This helps agents navigate multi-step workflows without hand-authored instructions — the service itself declares the dependency graph.

Binding requirements (v0.14)

Binding requirements declare that a capability needs a bound reference from a prior invocation before it can execute. This is the protocol mechanism for multi-step workflows like search, quote, then book — where the booking price should be locked to what the agent was quoted.

When to use binding

Binding is useful when a capability's cost depends on a prior step's output. Without binding, a capability with estimated cost and a budget constraint cannot be enforced — the service has no deterministic price to check against the budget. With binding, the quoted price becomes the check amount.

Declaration

A capability declares requires_binding in the manifest:

{
"book_flight": {
"description": "Book a flight reservation",
"requires_binding": [
{
"type": "quote",
"field": "quote_id",
"source_capability": "search_flights",
"max_age": "PT15M"
}
]
}
}
FieldTypeRequiredDescription
typestringYesBinding type (e.g., "quote", "reservation")
fieldstringYesParameter name that must be present at invocation
source_capabilitystringNoInformational — which capability produces this binding
max_agestring (ISO 8601 duration)NoMaximum age before the binding is stale

source_capability is informational only — the service does not validate it. It helps agents understand the expected workflow sequence.

Enforcement

When a capability declares requires_binding, the service enforces at invocation time:

  1. Missing binding: If the required field is absent from parameters, the service rejects with binding_missing.
  2. Stale binding: If max_age is declared and the binding has expired, the service rejects with binding_stale.

How binding reinforces budget enforcement

Binding and budget work together. For a capability with estimated cost:

  • Without binding: The service cannot reliably check the budget (the actual price is unknown). The service rejects with budget_not_enforceable.
  • With binding: The quoted/bound price becomes the check amount. If the bound price exceeds the budget, the service rejects with budget_exceeded. This makes estimated-cost capabilities budget-enforceable.

Control requirements (v0.14)

Control requirements are explicit pre-execution conditions that a capability declares. They tell both agents and services what must be true before invocation can proceed.

All control requirements are token-evaluable — they can be checked from the delegation token alone and are surfaced in /anip/permissions:

TypeCondition
cost_ceilingThe delegation token must carry constraints.budget
stronger_delegation_requiredThe token must have explicit capability binding

Declaration

{
"execute_trade": {
"description": "Execute a securities trade",
"control_requirements": [
{ "type": "cost_ceiling", "enforcement": "reject" },
{ "type": "stronger_delegation_required", "enforcement": "reject" }
]
}
}
FieldTypeRequiredDescription
typestringYesRequirement type
enforcementstringYes"reject" in v0.14 (reject invocation if not satisfied)

When enforcement is "reject", the service rejects invocations that do not satisfy the requirement, returning a control_requirement_unsatisfied failure.

Complete flow example

A travel service with budget-enforced booking through binding:

1. Agent obtains delegation token with budget: { currency: "USD", max_amount: 500 }

2. Agent invokes search_flights (read, no budget impact)
-> Returns flights with prices, including quote_id: "q-abc123"

3. Agent invokes book_flight with parameters: { quote_id: "q-abc123" }
-> Service checks: binding present? Yes (quote_id)
-> Service checks: binding fresh? Yes (within PT15M)
-> Service checks: bound price ($280) <= budget ($500)? Yes
-> Booking succeeds, cost_actual: { currency: "USD", amount: 280 }

4. Response includes budget_context:
{ budget_max: 500, budget_currency: "USD", cost_check_amount: 280, cost_certainty: "estimated" }

If the bound price had been $600, the service would reject with budget_exceeded before executing the booking. If the quote had expired, the service would reject with binding_stale.

Advisory composition hints (v0.17)

Advisory composition hints let a capability declare which other capabilities in the same manifest are naturally paired with it — without enforcing any ordering at the protocol level. These are hints for agents, not protocol constraints.

Fields

FieldTypeDescription
refresh_viastring[]Capabilities the agent should invoke to refresh a stale or expired artifact before retrying this capability
verify_viastring[]Capabilities the agent should invoke to verify side effects after executing this capability (especially useful for irreversible actions)

Both fields are optional, default to [], and every name in either list MUST refer to a capability declared in the same manifest.

When to use each hint

refresh_via — Use when the capability's success depends on a fresh artifact (quote, price lock, binding) that can become stale. An agent receiving binding_stale or budget_not_enforceable can use refresh_via to know exactly which capability to re-invoke for a fresh value.

{
"book_flight": {
"refresh_via": ["search_flights"]
}
}

verify_via — Use on capabilities with irreversible side effects, where an agent should verify what actually changed after execution. The hint guides the agent to confirm side effects without encoding them as hard protocol requirements.

{
"delete_resource": {
"verify_via": ["list_deployments"]
}
}

Same-manifest rule

All capability names in refresh_via and verify_via MUST be declared in the same manifest. References to capabilities on other services are not permitted — the hints are local to a single service's declaration graph.

Complete example

{
"search_flights": {
"description": "Search available flights",
"side_effect": { "type": "read" }
},
"book_flight": {
"description": "Book a confirmed flight reservation",
"side_effect": { "type": "irreversible" },
"refresh_via": ["search_flights"]
}
}

In this example: if book_flight fails with binding_stale, an agent reading refresh_via knows to re-invoke search_flights to get a fresh quote before retrying.

Cross-service handoff hints (v0.19)

Cross-service handoff hints extend the advisory composition model across service boundaries. Where refresh_via and verify_via reference capabilities in the same manifest, cross_service references capabilities on other services.

Fields

The cross_service object on a capability declaration carries four optional arrays:

ArrayTypeDescription
handoff_toServiceCapabilityRef[]Capabilities on other services this capability naturally leads into
refresh_viaServiceCapabilityRef[]Capabilities on other services that can refresh a stale artifact before retrying this capability
verify_viaServiceCapabilityRef[]Capabilities on other services that can verify side effects after executing this capability
followup_viaServiceCapabilityRef[]Capabilities on other services that are useful follow-up steps after this capability completes

Each entry is a ServiceCapabilityRef with two required fields:

FieldTypeDescription
servicestringThe service identifier of the target service
capabilitystringThe capability name on that service

When to use cross_service hints

handoff_to — Use when this capability produces output that is intended as input to a capability on another service. For example, a search service that produces quotes intended for a booking service on a different ANIP endpoint.

refresh_via — Use when the capability depends on a fresh artifact (quote, price lock) that may be produced by a capability on another service. An agent receiving binding_stale can follow this hint to refresh from the upstream service.

verify_via — Use on capabilities with irreversible side effects where a capability on another service can confirm the side effect occurred correctly.

followup_via — Use when this capability naturally triggers a subsequent step on another service as part of a multi-service workflow.

Example

{
"search_flights": {
"description": "Search available flights",
"side_effect": { "type": "read" },
"cross_service": {
"handoff_to": [
{ "service": "travel-booking", "capability": "book_flight" }
]
}
},
"book_flight": {
"description": "Book a confirmed flight reservation",
"side_effect": { "type": "irreversible" },
"cross_service": {
"refresh_via": [
{ "service": "travel-search", "capability": "search_flights" }
]
}
}
}

These hints are advisory only — the protocol does not enforce cross-service ordering. They guide agents in multi-service workflows without encoding hard protocol constraints.

Same-service vs cross-service hints

Hint typeScopeField
refresh_via (capability-level)Same manifestString array of capability names
verify_via (capability-level)Same manifestString array of capability names
cross_service.handoff_toOther servicesArray of ServiceCapabilityRef
cross_service.refresh_viaOther servicesArray of ServiceCapabilityRef
cross_service.verify_viaOther servicesArray of ServiceCapabilityRef
cross_service.followup_viaOther servicesArray of ServiceCapabilityRef

Next steps