Use this file to discover all available pages before exploring further.
There are two distinct ways a policy can stop an operation:
Denial — the policy blocked it outright. The agent is not permitted to do this at all.
Pending approval — the operation is allowed in principle but exceeds a threshold that requires the owner to sign off first. See Policy Engine for how to configure approval thresholds.
This guide covers the denial case. When a transfer or contract call violates a policy, the Cobo Agentic Wallet service returns a structured 403 response. The SDK raises a PolicyDeniedError carrying machine-readable guidance that enables agents to self-correct and retry without human intervention.
Each framework returns denials as tool output strings (not exceptions), so the LLM’s reasoning loop continues naturally:
LangChain
OpenAI Agents
MCP
# LangChain catches PolicyDeniedError inside the tool handler and# returns formatted text. The agent sees it as a normal tool result.toolkit = CoboAgentWalletToolkit(client=client)# No extra code needed — denial text arrives as tool output:# "Policy Denied [TRANSFER_LIMIT_EXCEEDED]: Amount exceeds per-tx limit# limit_value: 100# Suggestion: Retry with amount <= 100."
# OpenAI Agents SDK injects denial into the agent's system prompt# so the next turn has the constraint in context.context = create_cobo_agent_context()agent = create_cobo_agent(client, model="gpt-4.1-mini")# context.denial_messages is updated automatically on each denial
// Denial returned as isError: false — not a protocol error{ "content": [{ "type": "text", "text": "Policy Denied [TRANSFER_LIMIT_EXCEEDED]..." }], "isError": false}
Cumulative limits (daily, monthly) can’t be fixed by adjusting parameters. Escalate to the owner instead:
try: await agent_client.transfer_tokens( wallet_uuid=WALLET_UUID, chain_id="SETH", dst_addr="0x...", token_id="SETH_USDC", amount=amount, )except PolicyDeniedError as exc: if exc.denial.code in ("DAILY_CUMULATIVE_EXCEEDED", "MONTHLY_CUMULATIVE_EXCEEDED"): await notify_owner( f"Agent hit cumulative limit: {exc.denial.reason}\n" f"Remaining: {exc.denial.details.get('remaining', '?')}" ) return {"status": "escalated"} # Per-tx limits can still be self-corrected. amount = str(exc.denial.details.get("limit_value", amount))
Suggestion regex fallback
If details fields are missing, parse the suggestion text:
import refrom decimal import Decimaldef derive_retry_amount(denial, attempted: str) -> str | None: for key in ("limit_value", "max_allowed_amount"): if denial.details.get(key) is not None: v = str(denial.details[key]) if Decimal(v) < Decimal(attempted): return v match = re.search(r"<=\s*([0-9]+(?:\.[0-9]+)?)", denial.suggestion or "") if match: return match.group(1) return None