Customer Integration Guide
July 21, 2026 · View on GitHub
This guide is for integrating AVP into a real controlled-action workflow. It is not a demo path and does not bypass runtime safety.
For a first guided customer rollout, use
docs/PILOT_READINESS_CHECKLIST.md before running a controlled action.
What AVP Controls
AVP keeps the reputation stack and adds runtime enforcement:
Identity -> Cards/Reputation/Attestations -> Delegation
-> Runtime Gate -> Governance -> Human Approval
-> Signed Execution Receipt -> Remediation
Use can_trust() before selecting an agent. Use controlled_action() before the agent performs a concrete action.
Data Handling
AgentVeil separates local tools, hosted control/evidence APIs, and hosted content surfaces. Runtime Gate decisions should be based on bounded metadata, resource identifiers, hashes, and signed evidence. Do not place secrets, raw prompts, source code, private logs, credentials, or sensitive customer payloads in action names, resource names, metadata, denial reasons, support messages, job descriptions, or direct execution parameters unless the hosted workflow explicitly requires that content.
For the full model, see Data Handling.
Secrets
Client-side:
- Store the agent Ed25519 private key locally.
AVPAgent.save(passphrase=...)encrypts the key.- Never send private keys, API keys, cloud tokens, raw prompts, source code, or raw private logs to AVP.
Operator/backend:
ADMIN_TOKENprovisions trusted operator agents.CREDENTIAL_SIGNING_KEY_HEXsigns reputation credentials.EXECUTION_RECEIPT_SIGNING_KEY_HEXsigns execution receipts.HUMAN_APPROVAL_SIGNING_KEY_HEXsigns approval receipts.
If execution or human approval signing keys are missing, the backend fails closed with 503 before state changes.
Setup Checklist
Before the first controlled action:
- Create or load the local agent identity.
- Register and verify the agent DID with the AVP API.
- Obtain a DelegationReceipt for the intended action scope.
- Call
controlled_action(...). - Store
receipt_jcsif the action executes. - If approval is required, route approval to the principal and resume with
execute_after_approval(...). - If blocked, surface the reason and do not execute the action.
Creating a local DID/key is not enough by itself. The production API must know and verify that DID before signed runtime requests can succeed.
Integration Preflight
Run preflight before the first controlled action:
report = agent.integration_preflight()
if not report.ready:
print(report.status)
print(report.next_action)
Preflight checks API reachability, public DID registration status, verification status when visible, and one safe signed read request. It does not call Runtime Gate, approve actions, or execute actions.
DelegationReceipt Source
A DelegationReceipt is issued by the principal or workflow owner that authorizes the agent to request a bounded action. DelegationReceipt v1 names the agent DID, allowed category and financial predicates, and validity window. The requested action, resource, and environment are supplied to Runtime Gate and cross-checked there.
Use can_trust() before selecting an agent, then issue or obtain a DelegationReceipt for the selected agent before calling controlled_action(...). Reputation helps selection; delegation authorizes the runtime action.
For guided pilots, the principal can issue the current v1 receipt locally:
from datetime import timedelta
receipt = principal.issue_delegation_receipt(
agent_did=agent.did,
allowed_categories=["infrastructure"],
valid_for=timedelta(hours=1),
max_spend=None,
)
The principal signs with its local AVP identity. AVP never receives the principal private key.
DelegationReceipt v1 only emits predicates the current Runtime Gate enforces:
allowed_category and optional max_spend. It does not contain exact
allowed_actions, allowed_resources, or allowed_environments predicates.
Exact-scope issuance is a later protocol/backend phase.
The receipt is authority evidence, not execution permission by itself. Runtime Gate still evaluates the requested action, resource, environment, receipt validity, category, financial caps, Governance, and Human Approval before any execution path can proceed.
First Controlled Action
Use examples/first_controlled_action.py as the customer template. By default
it only loads identity and runs preflight. It calls controlled_action(...)
only when AVP_RUN_CONTROLLED_ACTION=1 and a DelegationReceipt is supplied via
AVP_DELEGATION_RECEIPT_FILE or AVP_DELEGATION_RECEIPT_JSON.
from agentveil import AVPAgent, ControlledActionOutcome
agent = AVPAgent.load("https://agentveil.dev", name="customer-agent", passphrase="...")
result: ControlledActionOutcome = agent.controlled_action(
action="infra.resource.inspect",
resource="resource:vol-123",
environment="development",
params={"resource_id": "vol-123"},
delegation_receipt=delegation_receipt,
)
if result.status == "executed":
receipt_jcs = result.receipt_jcs # exact signed proof artifact
receipt = result.receipt # parsed convenience view
elif result.status == "approval_required":
approval_id = result.approval["approval_id"]
elif result.status == "blocked":
reason = result.reason
controlled_action() never auto-approves. If approval is required, the principal must approve the request with their own DID.
Direct SDK params are a hosted execution surface. Keep them small and prefer
resource identifiers or hashes. For MCP clients, MCP Proxy is the preferred path
when raw tool arguments should remain local.
To export an explicit proof packet from local artifacts, call
build_proof_packet(...) after controlled_action(...):
packet = agent.build_proof_packet(
delegation_receipt=delegation_receipt,
outcome=result,
decision_receipt_jcs=decision_receipt_jcs, # recommended
approval_receipt_jcs=approval_receipt_jcs, # optional
remediation_case=remediation_case, # optional
)
proof_packet = packet.to_dict()
The helper does not fetch remote resources. It preserves raw signed receipt
text as decision_receipt_jcs, execution_receipt_jcs, and
approval_receipt_jcs, and includes parsed receipt fields only as a convenience
view. See Proof Packet Guide for export, save, reload, and
offline verification patterns.
Production applications should also catch SDK exceptions around
controlled_action(...), especially AVPRateLimitError, AVPValidationError,
and AVPServerError. The first-action template shows one minimal handling
pattern.
The first-action template intentionally does not generate a DelegationReceipt
for you. In production, the principal or workflow owner issues it after
selecting the agent and defining the allowed action scope. Put that signed
receipt in AVP_DELEGATION_RECEIPT_FILE or AVP_DELEGATION_RECEIPT_JSON.
Approval Resume Path
When controlled_action(...) returns approval_required, use
result.approval["approval_id"] and route the request to the principal. See
Approval Routing for polling, approve/deny, and resume
patterns.
receipt_result = agent.execute_after_approval(
audit_id=runtime_audit_id,
approval_id=approval_id,
action="infra.volume.delete",
resource="volume:vol-123",
environment="production",
params={"resource_id": "vol-123"},
)
receipt_jcs = receipt_result.receipt_jcs
Low-Level API Wrappers
Use these when your application wants to own orchestration:
runtime_evaluate(...)get_runtime_decision(audit_id)get_decision_receipt(audit_id)execute(...)get_execution_receipt(receipt_id)create_approval(...)get_approval(approval_id)approve(approval_id)deny(approval_id, reason=None)create_governance_policy(...)activate_governance_policy(policy_id)create_governance_risk_event(...)create_remediation_case(...)list_remediation_cases(...)get_remediation_case(case_id)add_remediation_evidence(...)
get_decision_receipt(), execute(), get_execution_receipt(), approve(),
and deny() return exact signed JSON text. Keep those strings for offline
proof.
Error Map
401: missing/invalid signature, nonce replay, expired timestamp, or unregistered agent.403: agent not verified, suspended, revoked, or not allowed for the requested role.404: missing or foreign private resource. AVP intentionally hides existence for private objects.409: valid request but unsafe/currently impossible state, such asapproval_required,approval_not_approved,approval_expired, orcapability_not_executable_in_mvp.422: schema validation error.429: Trust Gate or global rate limit. Respectretry_after.503: backend dependency unavailable or missing signing key. Do not retry aggressively.
Runtime BLOCK is not an HTTP error. It is a safety decision returned by Runtime Gate or Governance.
integration_preflight() normalizes setup/auth readiness states:
| Status | Meaning | Next action |
|---|---|---|
ready | API, registration, verification, and safe signed read are ready. | Attempt the controlled action through Runtime Gate. |
unregistered | DID is local-only or unknown to AVP. | Register and verify the DID. |
unverified_or_forbidden | DID exists but is not verified or cannot use the signed read path. | Complete verification or inspect permissions. |
agent_suspended | DID is suspended. | Stop using it until restored by an authorized operator. |
agent_revoked | DID is revoked. | Stop using it permanently. |
agent_migrated | DID has moved to a successor DID. | Switch to successor_did when available. |
signature_invalid | Signature failed or timestamp expired. | Check local key, clock skew, and signing path. |
nonce_replay | Signed headers or nonce were reused. | Retry with a fresh request. |
ready means setup/auth is ready to attempt controlled_action(...). It does
not guarantee Runtime Gate, Governance, Human Approval, or execution will allow
the requested action.
Proof Retention Checklist
For a security/compliance review, retain:
- DelegationReceipt
- Runtime Gate
audit_id - raw signed execution receipt (
receipt_jcs) - signed approval receipt, if used
- remediation case and evidence hashes, if contested
Also retain when available:
- agent DID and public key
- governance
policy_versionandpolicy_context_hash /v1/audit/verifyresult for audit-chain integrity
Signed execution and approval receipts are immutable proof artifacts. Remediation can reference them, but cannot rewrite them.
examples/first_controlled_action.py can write the explicit proof packet when
AVP_PROOF_PACKET_OUT is set. The generated packet is a retention helper, not
a replacement for the raw signed receipt strings.
Offline Proof Verification
Signature verification and AVP semantic verification are separate.
verify_signed_jcs(...) proves that one JCS receipt was signed by the DID in
its proof.verificationMethod:
from agentveil import verify_signed_jcs
verified = verify_signed_jcs(receipt_jcs, expected_signer_did=trusted_signer_did)
signer_did = verified["signer_did"]
body = verified["body"]
digest = verified["digest"]
For AVP-issued receipts, configure trusted backend signer DID(s). A structurally valid receipt signed by an untrusted DID must not be accepted as AVP proof.
verify_proof_packet(...) checks the AVP proof chain:
from agentveil import verify_proof_packet
verified_packet = verify_proof_packet(
proof_packet,
trusted_decision_signer_dids={trusted_decision_signer_did},
trusted_execution_signer_dids={trusted_execution_signer_did},
trusted_human_approval_signer_dids={trusted_human_approval_signer_did},
)
The packet verifier checks signed DecisionReceipt, HumanApprovalReceipt, and
ExecutionReceipt artifacts when present; verifies trusted backend signer DID(s);
compares cross-receipt hashes; and checks agent/action/resource/environment
linkage. trusted_backend_signer_dids={...} remains available as a
compatibility fallback for deployments that intentionally use one backend
signer for all AVP-issued receipt types.
Current receipt schema versions:
| Receipt | Current schema | Signer |
|---|---|---|
| DecisionReceipt | decision_receipt/3 | AVP backend decision/execution signer |
| HumanApprovalReceipt | human_approval_receipt/2 | AVP backend human approval signer |
| ExecutionReceipt | execution_receipt/2 | AVP backend decision/execution signer |
| DelegationReceipt | delegation context v1 | Principal DID |
Legacy receipt versions can still be signature-verified. Semantic verification is version-aware and does not require fields that did not exist in older signed schemas.
The Runtime Gate DecisionReceipt is now decision_receipt/3, a W3C Data
Integrity DataIntegrityProof / eddsa-jcs-2022 artifact. Verify a /3
receipt with verify_eddsa_jcs_2022(..., expected_signer_did=...), or — when
embedded in an evidence bundle — through the strict bundle verifier with an
externally pinned signer DID. The raw verify_signed_jcs(...) and
verify_proof_packet(...) paths above cover the legacy /1,/2 raw-JCS
decision-receipt schema and do not accept /3. verify_eddsa_jcs_2022(...)
requires an externally pinned signer DID; the document's own verificationMethod
is not trust authority. It is the SDK's own first-party Data Integrity verifier;
it is not a third-party standard-conformance certification, and only the decision
receipt uses Data Integrity — the other receipt families remain legacy raw-JCS.