Skip to main content

Protocol Reference

This page defines every ANIP endpoint, request/response schema, and data type. It is the implementation reference — use it alongside the concept pages (Capabilities, Delegation, Failures, Trust) which explain the why.

Endpoints

An ANIP HTTP service exposes 9 standard endpoints:

EndpointMethodAuthSection
/.well-known/anipGETNoneDiscovery
/.well-known/jwks.jsonGETNoneJWKS
/anip/manifestGETNoneManifest
/anip/tokensPOSTBearerToken Issuance
/anip/permissionsPOSTBearerPermission Discovery
/anip/invoke/{capability}POSTBearerInvocation
/anip/auditPOSTBearerAudit
/anip/checkpointsGETNoneCheckpoints
/anip/checkpoints/{id}GETNoneCheckpoint Detail

Discovery

GET /.well-known/anip

No authentication required. This is the entry point for any agent discovering the service.

Response

{
"anip_discovery": {
"version": "0.23.0",
"service_id": "travel-service",
"endpoints": {
"manifest": "/anip/manifest",
"tokens": "/anip/tokens",
"permissions": "/anip/permissions",
"invoke": "/anip/invoke/{capability}",
"audit": "/anip/audit",
"checkpoints": "/anip/checkpoints"
},
"capabilities": {
"search_flights": {
"description": "Search available flights",
"side_effect": { "type": "read" },
"minimum_scope": ["travel.search"],
"financial": false
},
"book_flight": {
"description": "Book a flight reservation",
"side_effect": { "type": "irreversible" },
"minimum_scope": ["travel.book"],
"financial": true
}
},
"trust": {
"level": "signed",
"anchoring": { "cadence": "hourly" }
},
"metadata_disclosure": {
"caller_class": "all",
"failure_detail": "full"
}
}
}

Fields

FieldTypeRequiredDescription
versionstringYesProtocol version (e.g., "0.23.0")
service_idstringYesUnique service identifier
endpointsobjectYesMap of operation names to URL paths
capabilitiesobjectYesLightweight capability summaries (name → metadata)
trustobjectYesTrust posture: level (declarative, signed, anchored) and optional anchoring cadence
metadata_disclosureobjectNoControls what metadata is disclosed to different caller classes

Each capability summary in discovery includes description, side_effect.type, minimum_scope[], and financial (boolean). The full declarations are in the manifest.


JWKS

GET /.well-known/jwks.json

No authentication required. Returns the service's public signing keys in standard JWKS format.

Response

{
"keys": [
{
"kty": "OKP",
"crv": "Ed25519",
"x": "dGhpcyBpcyBhIHB1YmxpYyBrZXk...",
"kid": "primary-signing-key",
"use": "sig"
}
]
}

Used to verify: manifest signatures (X-ANIP-Signature header), delegation token JWTs, and checkpoint signatures.


Manifest

GET /anip/manifest

No authentication required. Returns the full capability declarations with a cryptographic signature.

Response headers

X-ANIP-Signature: eyJhbGciOiJFZERTQSJ9...

Response body

{
"manifest_metadata": {
"version": "0.23.0",
"sha256": "a1b2c3d4...",
"issued_at": "2026-03-28T10:00:00Z",
"expires_at": "2026-03-29T10: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 (IATA code)"
},
{
"name": "destination",
"type": "airport_code",
"required": true,
"description": "Arrival airport (IATA code)"
},
{
"name": "date",
"type": "date",
"required": false,
"description": "Travel date (ISO 8601)"
}
],
"output": {
"type": "flight_list",
"fields": ["flight_number", "origin", "destination", "price"]
},
"side_effect": {
"type": "read"
},
"minimum_scope": ["travel.search"],
"cost": { "certainty": "fixed" },
"response_modes": ["unary"],
"observability": {
"logged": true,
"retention": "90d"
}
},
"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", "total_cost"]
},
"side_effect": {
"type": "irreversible"
},
"minimum_scope": ["travel.book"],
"cost": {
"certainty": "estimated",
"financial": {
"currency": "USD",
"range_min": 200,
"range_max": 800,
"typical": 420
}
},
"requires": [
{ "capability": "search_flights", "reason": "must verify flight exists" }
],
"response_modes": ["unary"],
"observability": {
"logged": true,
"retention": "365d",
"fields_logged": ["flight_number", "passengers"]
}
}
}
}

Capability declaration fields

FieldTypeRequiredDescription
descriptionstringYesHuman-readable description
contract_versionstringYesSemantic version of this capability's contract
inputsarrayYesInput parameter declarations (see below)
outputobjectYesOutput shape: type and fields[]
side_effectobjectYesSide-effect declaration (see below)
minimum_scopestring[]YesRequired scope strings for delegation
costobjectNoCost declaration (see below)
requiresarrayNoPrerequisite capabilities
response_modesstring[]No["unary"], ["streaming"], or ["unary", "streaming"]. Default: ["unary"]
observabilityobjectNoLogging and retention posture
refresh_viastring[]NoAdvisory: capabilities to re-invoke when a stale or missing artifact causes a failure. Every name must exist in the same manifest.
verify_viastring[]NoAdvisory: capabilities to invoke to verify side effects after executing this capability. Every name must exist in the same manifest.
cross_serviceobjectNoAdvisory cross-service handoff hints. See Cross-service handoff hints (v0.19).

Input field

FieldTypeRequiredDescription
namestringYesParameter name
typestringYesSemantic type hint (e.g., "string", "airport_code", "integer")
requiredbooleanNoDefault: true
defaultanyNoDefault value if not provided
descriptionstringNoHuman-readable description

Side-effect types

ValueMeaning
readNo state change. Safe to call speculatively.
writeReversible state change.
transactionalState change with a rollback window. Has optional rollback_window (duration) and compensation (capability name).
irreversiblePermanent state change. Cannot be undone.

Cost declaration

FieldTypeDescription
certaintystring"fixed", "estimated", or "variable"
financialobjectOptional: currency, range_min, range_max, typical
computeobjectOptional: expected_duration, resource hints
determined_bystringOptional: what determines the cost (e.g., "passenger_count")

Cross-service hints (v0.19)

The cross_service object contains four optional arrays, each of type ServiceCapabilityRef[]:

ArrayDescription
handoff_toCapabilities on other services this capability naturally leads into
refresh_viaCapabilities on other services that refresh a stale artifact
verify_viaCapabilities on other services that verify side effects
followup_viaCapabilities on other services useful after this one completes

ServiceCapabilityRef (v0.19)

FieldTypeRequiredDescription
servicestringYesThe service identifier of the target service
capabilitystringYesThe capability name on that service

Token Issuance

POST /anip/tokens

Authentication: Bearer token (API key for bootstrap, existing JWT for delegation).

Request

{
"scope": ["travel.search", "travel.book"],
"capability": "book_flight",
"subject": "agent-007",
"purpose_parameters": {
"task_id": "trip-planning-2026",
"budget_usd": 500
},
"ttl_hours": 2
}
FieldTypeRequiredDescription
scopestring[]YesRequested scope strings
capabilitystringNoSpecific capability this token is for
subjectstringYes (bootstrap)Subject identifier for the delegated principal
purpose_parametersobjectNoOpaque purpose constraints (budget, task context)
ttl_hoursnumberNoToken lifetime in hours. Default: 2

Response

{
"issued": true,
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...",
"scope": ["travel.search", "travel.book"],
"capability": "book_flight",
"expires_at": "2026-03-28T12:00:00Z"
}

Budget constraints

The token issuance request can include a budget field, which the service stores as constraints.budget in the JWT claims:

{
"scope": ["travel.search", "travel.book"],
"subject": "agent-007",
"budget": {
"currency": "USD",
"max_amount": 500
},
"ttl_hours": 2
}
FieldTypeRequiredDescription
budget.currencystringYesISO 4217 currency code
budget.max_amountnumberYesMaximum spend ceiling

Budget narrowing rule: When delegating from a parent token, the child budget MUST NOT exceed the parent's budget. If the parent has max_amount: 500, the child cannot request max_amount: 600. Currency must match. If the parent has no budget, the child MAY introduce one.

Delegation rules

  • Scope can only narrow, never widen. A delegated token cannot have scope the parent doesn't have.
  • Budget can only narrow, never widen. The token's constraints.budget is the enforceable ceiling.
  • The service signs tokens with its Ed25519 key pair. Any replica with the same key material can verify them.

Permission Discovery

POST /anip/permissions

Authentication: Bearer JWT delegation token.

Request

{}

Response

{
"available": [
{
"capability": "search_flights",
"scope_match": "travel.search",
"constraints": {}
}
],
"restricted": [
{
"capability": "book_flight",
"reason": "missing scope: travel.book",
"grantable_by": "human:[email protected]"
}
],
"denied": [
{
"capability": "admin_reset",
"reason": "requires admin principal class"
}
]
}

Three-bucket model

BucketMeaning
availableToken has sufficient scope. Fields: capability, scope_match, constraints
restrictedMissing a grantable scope. Fields: capability, reason, reason_type, grantable_by, unmet_token_requirements, resolution_hint
deniedStructurally impossible (wrong principal class). Fields: capability, reason, reason_type

Permission response fields — restricted entry (v0.15)

FieldTypeRequiredDescription
capabilitystringYesCapability name
reasonstringYesHuman-readable explanation of the restriction
reason_typestringYesMachine-readable restriction category (see below)
grantable_bystringNoPrincipal who can grant the missing authority
unmet_token_requirementsstring[]NoUnsatisfied control_requirements types
resolution_hintstringNoShort actionable suggestion for resolving the restriction

Permission response fields — denied entry (v0.15)

FieldTypeRequiredDescription
capabilitystringYesCapability name
reasonstringYesHuman-readable explanation of the denial
reason_typestringYesMachine-readable denial category (see below)

reason_type values

ValueWhen used
insufficient_scopeToken lacks one or more required scope strings
insufficient_delegation_depthDelegation chain is too deep for this capability
stronger_delegation_requiredToken needs explicit capability binding or tighter purpose constraints
unmet_control_requirementToken does not satisfy a declared control requirement
non_delegableCapability requires the direct (root) principal; delegated agents are blocked

Unmet token requirements (v0.14)

When a capability declares control_requirements with token-evaluable types (cost_ceiling, stronger_delegation_required), and the caller's token does not satisfy them, the capability appears in restricted with an unmet_token_requirements array listing the unsatisfied requirement types:

{
"restricted": [
{
"capability": "execute_trade",
"reason": "missing control requirements: cost_ceiling",
"grantable_by": "human:[email protected]",
"unmet_token_requirements": ["cost_ceiling"]
}
]
}

All control requirements are token-evaluable and surfaced in permission discovery.


Invocation

POST /anip/invoke/{capability}

Authentication: Bearer JWT delegation token.

Request

{
"parameters": {
"origin": "SEA",
"destination": "SFO"
},
"client_reference_id": "task:abc/step-3",
"task_id": "trip-planning-2026",
"parent_invocation_id": "inv-a1b2c3d4e5f6",
"stream": false
}
FieldTypeRequiredDescription
parametersobjectYesCapability input parameters
client_reference_idstringNoCaller-supplied correlation ID (max 256 chars), echoed in response
task_idstringNoTask/workflow identity for grouping related invocations (max 256 chars). If the delegation token has purpose.task_id, must match or be omitted.
parent_invocation_idstringNoReference to the invocation that triggered this one (format: inv-{hex12}). Syntactically validated, not referentially.
upstream_servicestringNoIdentifies the ANIP service that initiated this call as part of a cross-service workflow. Echoed in response and recorded in audit. (v0.18)
streambooleanNoRequest streaming response (SSE). Default: false

Success response (HTTP 200)

{
"success": true,
"invocation_id": "inv-7f3a2b4c5d6e",
"client_reference_id": "task:abc/step-3",
"task_id": "trip-planning-2026",
"parent_invocation_id": "inv-a1b2c3d4e5f6",
"result": {
"flights": [
{ "flight_number": "AA100", "price": 420 },
{ "flight_number": "DL310", "price": 280 }
]
},
"cost_actual": {
"currency": "USD",
"amount": 0
}
}

Failure response (HTTP 4xx)

{
"success": false,
"invocation_id": "inv-8b2f4a7c9d0e",
"task_id": "trip-planning-2026",
"parent_invocation_id": "inv-a1b2c3d4e5f6",
"client_reference_id": "task:abc/step-3",
"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"
}
}
}

Response fields

FieldTypeAlways presentDescription
successbooleanYesWhether the invocation succeeded
invocation_idstringYesServer-generated unique ID (inv_{hex12})
client_reference_idstringIf provided in requestEchoed caller correlation ID
task_idstringIf provided or from token purposeTask/workflow identity
parent_invocation_idstringIf provided in requestEchoed parent invocation reference
upstream_servicestringIf provided in requestEchoed upstream service identifier (v0.18)
resultobjectOn successCapability-specific result data
cost_actualobjectIf capability has financial costcurrency and amount
failureobjectOn failureStructured failure (see below)

Budget enforcement (v0.14)

The service enforces budget constraints from the delegation token's constraints.budget before executing the handler. Budget enforcement is pre-execution and deterministic — there is no post-execution "blessed overspend."

The check amount depends on cost certainty and whether a binding is present:

Cost certaintyBinding present?Check amountFailure on exceed
fixedN/Acost.financial.amountbudget_exceeded
estimatedYes (via requires_binding)Bound pricebudget_exceeded
estimatedNoN/A — reject immediatelybudget_not_enforceable
dynamicN/Acost.financial.upper_boundbudget_exceeded

If the token budget's currency does not match the capability's cost.financial.currency, the service rejects with budget_currency_mismatch.

When a budget was evaluated (success or failure), the response includes a budget_context object:

{
"budget_context": {
"budget_max": 500,
"budget_currency": "USD",
"cost_check_amount": 280,
"cost_certainty": "estimated"
}
}

ANIPFailure schema

FieldTypeRequiredDescription
typestringYesMachine-readable failure category (e.g., insufficient_scope, budget_exceeded, rate_limited)
detailstringYesHuman-readable explanation
retrybooleanYesWhether retrying the same call might succeed. Default: true
resolutionobjectYesRecovery guidance (see below)

Resolution schema

FieldTypeRequiredDescription
actionstringYesWhat to do (e.g., request_broader_scope, request_budget_increase, wait_and_retry)
recovery_classstringYesCoarse recovery strategy (see vocabulary below). Advisory — does not override retry.
requiresstringNoWhat's needed to resolve
grantable_bystringNoWho can grant what's needed (principal identifier)
estimated_availabilitystringNoHow soon resolution is possible (e.g., immediate, 24h)

recovery_class vocabulary (v0.16)

recovery_classMeaning
retry_nowRetry immediately — no external change required.
wait_then_retryWait for a time-bounded condition, then retry.
refresh_then_retryRefresh a local artifact (binding, quote, token) and retry.
redelegation_then_retryObtain a new or modified delegation token, then retry.
revalidate_then_retryRe-fetch and validate service-side state before retrying.
terminalNo automated recovery — requires human escalation. Always paired with retry: false.

Failure types — authority (v0.15)

TypeWhenRetryTypical resolutionrecovery_class
non_delegable_actionCapability requires the root principal; a delegated agent attempted itNoinvoke_as_root_principal — the human must invoke directlyterminal

Canonical resolution actions (v0.16)

Five new canonical resolution.action values added in v0.16, completing the full action vocabulary:

resolution.actionrecovery_classWhen used
retry_nowretry_nowTransient condition; safe to retry immediately without any change
provide_credentialsretry_nowCredentials are missing or need refreshing but no delegation change is required
wait_and_retrywait_then_retryRate-limit, cooldown, or time-bounded unavailability
revalidate_staterevalidate_then_retryService-side state has changed; re-fetch and verify before retrying
check_manifestrevalidate_then_retryCapability graph or manifest may be stale; re-fetch manifest and retry

Failure types — budget, binding, and control (v0.14)

TypeWhenRetryTypical resolutionrecovery_class
budget_exceededCost exceeds the delegated budgetNorequest_budget_increase — obtain a higher budget delegationredelegation_then_retry
budget_currency_mismatchBudget and cost currencies differNoobtain_matching_currency — re-delegate with matching currencyredelegation_then_retry
budget_not_enforceableEstimated cost with no binding to pin a priceNoobtain_quote_first — invoke the source capability to get a bound pricerefresh_then_retry
binding_missingRequired binding field absent from parametersNoobtain_binding — invoke the source capability firstrefresh_then_retry
binding_staleBinding exceeded max_ageYesrefresh_binding — re-invoke the source capability for a fresh quoterefresh_then_retry
control_requirement_unsatisfiedA declared control requirement is not metNoDepends on requirement type (e.g., obtain budget delegation for cost_ceiling)redelegation_then_retry

Audit

POST /anip/audit

Authentication: Bearer token. Results are scoped to the root principal of the caller's delegation chain — a principal can only see its own audit trail.

Request (query parameters)

ParameterTypeDescription
capabilitystringFilter by capability name
sincestringISO 8601 timestamp — entries after this time
invocation_idstringFilter by specific invocation
client_reference_idstringFilter by caller correlation ID
task_idstringFilter by task identity
parent_invocation_idstringFilter by parent invocation
limitintegerMaximum entries to return

Response

{
"entries": [
{
"invocation_id": "inv_7f3a2b4c5d6e",
"capability": "search_flights",
"actor_key": "agent:booking-bot",
"root_principal": "human:[email protected]",
"event_class": "low_risk_success",
"success": true,
"client_reference_id": "task:abc/step-3",
"task_id": "trip-planning-2026",
"parent_invocation_id": "inv-a1b2c3d4e5f6",
"timestamp": "2026-03-28T10:30:00Z"
}
]
}

Event classification

ClassWhen used
low_risk_successRead capability succeeded
high_risk_successWrite/irreversible/financial capability succeeded
low_risk_failureRead capability failed
high_risk_failureWrite/irreversible/financial capability failed

Checkpoints

GET /anip/checkpoints

No authentication required.

Query parameters

ParameterTypeDescription
limitintegerMaximum checkpoints to return. Default: 20

Response

{
"checkpoints": [
{
"checkpoint_id": "cp_a1b2c3",
"sequence": 42,
"merkle_root": "sha256:7d3f8a...",
"entry_count": 150,
"created_at": "2026-03-28T11:00:00Z",
"signature": "eyJhbGciOi..."
}
]
}

Checkpoint Detail

GET /anip/checkpoints/{id}

Returns a single checkpoint with optional consistency proof fields.

Response

{
"checkpoint_id": "cp_a1b2c3",
"sequence": 42,
"merkle_root": "sha256:7d3f8a...",
"entry_count": 150,
"created_at": "2026-03-28T11:00:00Z",
"signature": "eyJhbGciOi...",
"tree_size": 150,
"tree_head": "sha256:7d3f8a..."
}

JSON Schema

Machine-readable JSON Schema definitions are maintained alongside the spec:

The spec (SPEC.md) is authoritative for semantics. The schemas are authoritative for structure.