# Conto Documentation - Complete Reference > Conto is an enterprise control center for managing AI agent financial transactions using stablecoins. ## Quick Reference - Base URL: https://conto.finance - API Base: https://conto.finance/api - OpenAPI Spec: https://conto.finance/api/openapi - SDK Package: @conto/sdk - Dashboard: https://conto.finance --- # Core Concepts Before integrating with Conto, it's helpful to understand the key concepts and terminology. ## Agents An **Agent** represents an AI agent connected to Conto. Each agent has: - **Name** - Human-readable identifier - **Type** - The agent framework (OpenAI, Claude, LangChain, Custom) - **Public Key** - Unique cryptographic identifier - **Status** - Active, Paused, Suspended, or Revoked - **SDK Keys** - API keys for authentication ```typescript // Example agent configuration { name: "Customer Support Agent", agentType: "OPENAI_ASSISTANT", publicKey: "0x1234...", status: "ACTIVE" } ``` ## Wallets A **Wallet** holds stablecoins for agent payments. Wallets can be linked to multiple agents with different spend limits. | Property | Description | | ------------- | --------------------------- | | `address` | Wallet address | | `balance` | Current stablecoin balance | | `chainType` | EVM or SOLANA | | `custodyType` | PRIVY, SPONGE, or EXTERNAL | | `status` | ACTIVE, FROZEN, or ARCHIVED | ### Supported Chains | Chain | Chain ID | Type | Currency | Environment | | -------------- | ---------------- | ------ | -------- | ----------------- | | Tempo Testnet | `42431` | EVM | pathUSD | Testnet (default) | | Tempo Mainnet | `4217` | EVM | USDC.e | Production | | Base | `8453` | EVM | USDC | Production | | Base Sepolia | `84532` | EVM | USDC | Testnet | | Ethereum | `1` | EVM | USDC | Production | | Arbitrum One | `42161` | EVM | USDC | Production | | Polygon | `137` | EVM | USDC | Production | | Solana Mainnet | `solana-mainnet` | Solana | USDC | Production | | Solana Devnet | `solana-devnet` | Solana | USDC | Testnet | Info: Tempo chains require no separate gas token. Tempo Testnet uses `pathUSD`; Tempo Mainnet uses `USDC.e`, making Tempo the easiest option for both testing and production. ## Cards A **Card** is a payment card (virtual or physical) that agents can use for traditional merchant payments. Cards are issued through **Stripe Issuing** or **Lithic**, or registered manually for BYOC flows. | Property | Description | | ----------------- | --------------------------------- | | `provider` | STRIPE_ISSUING, LITHIC, or MANUAL | | `last4` | Last 4 digits of card number | | `cardType` | VIRTUAL or PHYSICAL | | `status` | ACTIVE, PAUSED, or CANCELLED | | `spendLimitDaily` | Card-level daily limit | Cards are assigned to agents via **Agent-Card Links**, similar to wallet links. Each agent gets independent spend limits, time windows, and merchant category controls on the shared card. Policies can also be linked directly to cards for rule engine enforcement. ## Agent-Wallet Links When you link an agent to a wallet, you configure: - **Spend Limits** - Per-transaction, daily, weekly, monthly limits - **Time Windows** - Allowed hours and days for transactions - **Delegation Type** - Full, Limited, View-Only, Preapproved, or Allowlist access ```typescript // Example agent-wallet link (business-hours restriction) { spendLimitPerTx: 500, // Max $500 per transaction spendLimitDaily: 2000, // Max $2,000 per day spendLimitMonthly: 20000, // Max $20,000 per month allowedHoursStart: 9, // 9 AM (default: 0) allowedHoursEnd: 17, // 5 PM (default: 24) allowedDays: ["Mon", "Tue", "Wed", "Thu", "Fri"] // default: all 7 days } ``` ## Policies A **Policy** is a set of rules that determine whether a payment should be approved, denied, or require manual approval. ### Policy Types ### Spend Limit Control maximum amounts per transaction, day, week, or month ### Time Window Restrict transactions to specific hours and days ### Counterparty Control which recipients are allowed based on trust ### Geographic Block transactions to sanctioned countries (OFAC compliance) ### Category Allow or block specific spending categories ### Approval Threshold Require manual approval above certain amounts ### Velocity Limit transaction frequency to prevent rapid drain ### x402 / MPP Controls for micropayment protocols (price ceilings, service allowlists) ### Policy Priority Policies are evaluated in priority order (highest first). This allows you to: 1. Create override policies (priority 90+) for critical rules 2. Set default policies (priority 50) for normal operations 3. Add catch-all policies (priority 10) for fallbacks ## Transactions A **Transaction** represents a stablecoin payment from a wallet to a recipient. ### Transaction Lifecycle ```mermaid stateDiagram-v2 [*] --> Pending: Request Created Pending --> Approved: Policy Passed Pending --> Denied: Policy Failed Pending --> RequiresApproval: Needs Human Review RequiresApproval --> Approved: Approved RequiresApproval --> Denied: Rejected Approved --> Executing: Execute Called Executing --> Completed: Tx Submitted Completed --> Confirmed: Block Confirmed Executing --> Failed: Tx Failed Confirmed --> [*] Denied --> [*] Failed --> [*] ``` ### Transaction Properties | Property | Description | | -------------- | --------------------------- | | `txHash` | Blockchain transaction hash | | `amount` | Payment amount | | `status` | Current transaction status | | `policyResult` | Policy evaluation result | | `purpose` | Reason for payment | | `category` | Spending category | ## Counterparties A **Counterparty** is a recipient of payments (vendor, merchant, agent, service, or platform). ### Trust Levels | Level | Score Range | Description | | ------------ | ----------- | --------------------------------------------- | | **TRUSTED** | 75 - 100 | High confidence, minimal restrictions | | **VERIFIED** | 50 - 74 | Established relationship, standard monitoring | | **UNKNOWN** | 20 - 49 | New or limited history, requires scrutiny | | **BLOCKED** | 0 - 19 | High risk, all transactions blocked | ### Trust Score Factors Trust scores are calculated based on: - **History (30%)** - Transaction count and volume - **Reliability (30%)** - Success rate, failed transactions - **Activity (20%)** - Account age, recency, consistency - **Verification (20%)** - Manual verification, network data, and [external trust providers](https://conto.finance/docs/integrations/trust-providers) (Fairscale for Solana, sanctions screening) Conto maintains both a **per-organization** trust score (your org's own view of a counterparty) and a **network** trust score (the cross-organization view). Policies can reference either via the `TRUST_SCORE` rule type. See [Trust Scoring](https://conto.finance/docs/guides/trust-scoring) for the full breakdown. ## Network Intelligence Conto's **Network Intelligence** provides anonymized cross-organization trust data: - See if other organizations have flagged an address - Benefit from collective fraud detection - Contribute to network safety (opt-in) For **Solana wallets**, trust scores are powered by [Fairscale](https://fairscale.xyz) — a composable reputation scoring system that analyzes onchain behavioral signals like transaction patterns, staking activity, and token holdings. Solana addresses with no existing network data are automatically enriched with Fairscale scores, so you get real trust data instead of blank defaults. Conto also integrates with sanctions screening providers (Chainalysis, TRM Labs, and a built-in OFAC list) for compliance. See [Trust & Risk Providers](https://conto.finance/docs/integrations/trust-providers) for full details on all integrated providers. Note: Network Intelligence data is anonymized. Organizations share aggregate trust signals, not transaction details. ## SDK Keys vs API Keys Conto uses two types of authentication: | Credential | Format | Scope | Typical use | | ------------------------ | -------------------- | -------------------------- | ----------------------------------------------------- | | **Standard SDK key** | `conto_agent_xxx...` | One agent | Payment requests and agent-scoped reads | | **Admin SDK key** | `conto_agent_xxx...` | One agent, elevated scopes | Delegated management of agents, wallets, and policies | | **Organization API key** | `conto_xxx...` | Whole organization | Backend/admin automation with `ContoAdmin` | - **Standard SDK keys** default to least-privilege access: `payments:request` plus read-only agent data. - **Admin SDK keys** add elevated `agents:write`, `wallets:write`, and `policies:write` scopes but remain tied to a specific agent identity. - **Organization API keys** are the canonical credential for backend provisioning, integrations, and the `ContoAdmin` SDK. ## Next Steps ### Quick Start Link: https://conto.finance/docs/quickstart/setup Set up your first agent ### Policy Guide Link: https://conto.finance/docs/policies/overview Configure spending policies --- # Welcome to Conto Conto is an enterprise control center for managing AI agent financial transactions using stablecoins, cards, and micropayment protocols (x402 and MPP). ### Quick Start Link: https://conto.finance/docs/quickstart/setup Get your first agent making payments in under 5 minutes ### SDK Reference Link: https://conto.finance/docs/sdk/installation Full TypeScript SDK documentation ### API Reference Link: https://conto.finance/api-docs Complete REST API documentation ### Policies Link: https://conto.finance/docs/policies/overview Configure spending rules and limits ## What is Conto? Conto provides enterprise-grade controls for AI agent spending: - **Agent Identity Management** - Register and manage AI agents with unique identities - **Wallet Management** - Create and fund wallets on Base, Solana, or Tempo with configurable spend limits - **Policy Engine** - Define spending rules, limits, and approval workflows - **Payment Protocols** - Support for stablecoins, x402 micropayments, MPP sessions, A2A transfers, and card payments - **Transaction Monitoring** - Real-time visibility into all agent transactions - **Trust Intelligence** - Cross-organization trust scoring for counterparties ## How It Works ```mermaid sequenceDiagram participant Agent participant Conto participant Blockchain Agent->>Conto: Request Payment Conto->>Conto: Evaluate Policies alt Approved Conto->>Agent: Approval + Request ID Agent->>Conto: Execute Payment Conto->>Blockchain: Submit Transaction Blockchain->>Conto: Confirmation Conto->>Agent: Transaction Hash else Denied Conto->>Agent: Denial + Reasons else Requires Approval Conto->>Agent: Pending + Request ID end ``` ## Key Features Every payment request is evaluated against your configured policies before execution. Set spend limits, time windows, approved vendors, and more. Manage multiple AI agents with different permissions, wallets, and spending limits. Perfect for teams with multiple autonomous agents. Full visibility into agent transactions with real-time dashboards, alerts, and audit logs. Benefit from anonymized cross-organization trust data to identify risky counterparties before transacting. ## Supported Integrations | Integration | Description | |------------|-------------| | **TypeScript SDK** | Native SDK for Node.js and browser environments | | **REST API** | Full programmatic access to all platform features | | **MCP Server** | Zero-code integration for Claude Desktop | | **Nous Hermes Skill** | Policy enforcement skill for [Nous Hermes](https://conto.finance/docs/sdk/hermes) agents | | **OpenClaw Skill** | Policy enforcement skill for [OpenClaw](https://conto.finance/docs/sdk/openclaw) agents | | **LangChain** | Tool integration for LangChain agents | | **OpenAI** | Function calling for GPT assistants | ## Next Steps ### Create Account Link: https://conto.finance Sign up for Conto and create your organization ### Quick Start Guide Link: https://conto.finance/docs/quickstart/setup Follow our step-by-step setup guide ## Get in Touch Have questions, feedback, or need help? We'd love to hear from you. ### Discord Community Link: https://discord.gg/h7rYrpkrRt Join our Discord to chat with the team and other users ### Contact Support Link: mailto:support@conto.finance Email the team at support@conto.finance --- # Connecting Agents via API This guide shows how to connect different AI agent frameworks to Conto, enabling them to request, approve, execute, and confirm payments depending on the wallet model you choose. Info: Before integrating, make sure you've [connected your agent, linked a wallet, and generated an SDK key](https://conto.finance/docs/quickstart/first-agent). ## SDK Authentication All SDK endpoints require the agent's SDK key in the `Authorization` header: ``` Authorization: Bearer conto_agent_xxxxx... ``` ## Choose Your Wallet Flow First Before wiring up tool calls, decide whether your agent uses a managed wallet or an external wallet. | Wallet model | Who holds the keys | Endpoints | What Conto can stop | | ------------------------------- | ------------------------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------- | | **Managed** (`PRIVY`, `SPONGE`) | The custody provider | `request -> execute` | Conto stays in the execution path and can block the spend | | **External** (`EXTERNAL`) | You, your agent, or your wallet stack | `approve -> transfer -> confirm` | Conto can gate the Conto-routed flow, but cannot block a direct self-signed transfer outside Conto | The framework examples below assume a managed wallet and use `request -> execute`. If your agent holds the signing keys, switch to `approve -> transfer -> confirm` instead. See [Custody Modes](https://conto.finance/docs/guides/custody-modes) for the full breakdown. ## Register The Wallet The Right Way If your integration already has a wallet, register that same wallet in Conto instead of creating a second one. - For an existing **Privy** wallet, create the Conto wallet with `custodyType=PRIVY`, `externalWalletId`, and `address`. Conto will attach or reuse the wallet record instead of minting a new Privy wallet. - For a self-custodied wallet or MPC stack outside Conto, register it as `custodyType=EXTERNAL` with its `address`. - Wallet registration is idempotent per `externalWalletId + chainId` or `address + chainId`, so your backend can safely retry the request. ## Framework Integrations ### OpenAI Assistants ```typescript import OpenAI from 'openai'; const openai = new OpenAI(); const CONTO_SDK_KEY = process.env.CONTO_SDK_KEY; // Define the payment tool for your assistant const tools = [ { type: 'function', function: { name: 'make_payment', description: 'Make a payment to a recipient', parameters: { type: 'object', properties: { amount: { type: 'number', description: 'Payment amount in USD' }, recipientAddress: { type: 'string', description: 'Recipient wallet address' }, recipientName: { type: 'string', description: 'Recipient name' }, purpose: { type: 'string', description: 'Payment purpose' }, }, required: ['amount', 'recipientAddress', 'purpose'], }, }, }, ]; // Handle the payment tool call async function handlePayment(args: { amount: number; recipientAddress: string; recipientName?: string; purpose: string; }) { // Step 1: Request payment authorization const requestResponse = await fetch('https://conto.finance/api/sdk/payments/request', { method: 'POST', headers: { Authorization: `Bearer ${CONTO_SDK_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ amount: args.amount, recipientAddress: args.recipientAddress, recipientName: args.recipientName, purpose: args.purpose, category: 'OPERATIONS', }), }); const request = await requestResponse.json(); if (request.status === 'DENIED') { return { success: false, error: request.reasons.join(', ') }; } if (request.status === 'REQUIRES_APPROVAL') { return { success: false, pending: true, message: 'Payment requires manual approval' }; } // Step 2: Execute the approved payment const executeResponse = await fetch( `https://conto.finance/api/sdk/payments/${request.requestId}/execute`, { method: 'POST', headers: { Authorization: `Bearer ${CONTO_SDK_KEY}` }, } ); const result = await executeResponse.json(); return { success: true, transactionHash: result.txHash, amount: result.amount, explorerUrl: result.explorerUrl, }; } ``` In your OpenAI chat loop, check for `tool_calls` in the response. When the model calls `make_payment`, extract the arguments, call the Conto API, and send the result back as a tool response message. ### Anthropic Claude (Tool Use) ```typescript import Anthropic from '@anthropic-ai/sdk'; const anthropic = new Anthropic(); const CONTO_SDK_KEY = process.env.CONTO_SDK_KEY; const tools = [ { name: 'make_payment', description: 'Make a payment to a recipient using Conto', input_schema: { type: 'object', properties: { amount: { type: 'number', description: 'Payment amount in USD' }, recipientAddress: { type: 'string', description: 'Recipient wallet address' }, recipientName: { type: 'string', description: 'Recipient name' }, purpose: { type: 'string', description: 'Payment purpose' }, }, required: ['amount', 'recipientAddress', 'purpose'], }, }, ]; async function runAgent(userMessage: string) { const response = await anthropic.messages.create({ model: 'claude-sonnet-4-20250514', max_tokens: 1024, tools: tools, messages: [{ role: 'user', content: userMessage }], }); for (const block of response.content) { if (block.type === 'tool_use' && block.name === 'make_payment') { const result = await executeContoPayment(block.input); // Continue conversation with tool result... } } } async function executeContoPayment(input: any) { const request = await fetch('https://conto.finance/api/sdk/payments/request', { method: 'POST', headers: { Authorization: `Bearer ${CONTO_SDK_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify(input), }).then((r) => r.json()); if (request.status !== 'APPROVED') { return { error: request.reasons?.join(', ') || 'Payment not approved' }; } const result = await fetch( `https://conto.finance/api/sdk/payments/${request.requestId}/execute`, { method: 'POST', headers: { Authorization: `Bearer ${CONTO_SDK_KEY}` }, } ).then((r) => r.json()); return result; } ``` ### LangChain ```typescript import { Tool } from '@langchain/core/tools'; const CONTO_SDK_KEY = process.env.CONTO_SDK_KEY; class ContoPaymentTool extends Tool { name = 'conto_payment'; description = 'Make a payment using Conto. Input should be JSON with amount, recipientAddress, and purpose.'; async _call(input: string): Promise { const params = JSON.parse(input); const request = await fetch('https://conto.finance/api/sdk/payments/request', { method: 'POST', headers: { Authorization: `Bearer ${CONTO_SDK_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ amount: params.amount, recipientAddress: params.recipientAddress, purpose: params.purpose, category: params.category || 'OPERATIONS', }), }).then((r) => r.json()); if (request.status === 'DENIED') { return JSON.stringify({ error: `Payment denied: ${request.reasons.join(', ')}` }); } if (request.status === 'REQUIRES_APPROVAL') { return JSON.stringify({ pending: true, message: 'Payment requires manual approval', requestId: request.requestId, }); } const result = await fetch( `https://conto.finance/api/sdk/payments/${request.requestId}/execute`, { method: 'POST', headers: { Authorization: `Bearer ${CONTO_SDK_KEY}` }, } ).then((r) => r.json()); return JSON.stringify({ success: true, transactionHash: result.txHash, amount: result.amount, explorerUrl: result.explorerUrl, }); } } ``` ### Python (Any Framework) ```python import os import requests CONTO_SDK_KEY = os.environ.get('CONTO_SDK_KEY') CONTO_API_URL = 'https://conto.finance/api' def make_payment(amount: float, recipient_address: str, purpose: str, recipient_name: str = None): """Make a payment via Conto API""" headers = { 'Authorization': f'Bearer {CONTO_SDK_KEY}', 'Content-Type': 'application/json' } # Step 1: Request authorization request_payload = { 'amount': amount, 'recipientAddress': recipient_address, 'purpose': purpose, 'recipientName': recipient_name, 'category': 'OPERATIONS' } response = requests.post( f'{CONTO_API_URL}/sdk/payments/request', headers=headers, json=request_payload ) request_data = response.json() if request_data.get('status') == 'DENIED': return {'success': False, 'error': ', '.join(request_data.get('reasons', []))} if request_data.get('status') == 'REQUIRES_APPROVAL': return {'success': False, 'pending': True, 'requestId': request_data['requestId']} # Step 2: Execute the payment execute_response = requests.post( f'{CONTO_API_URL}/sdk/payments/{request_data["requestId"]}/execute', headers=headers ) result = execute_response.json() return { 'success': True, 'transactionHash': result.get('txHash'), 'amount': result.get('amount'), 'explorerUrl': result.get('explorerUrl') } ``` ## Payment Endpoints Reference | Endpoint | Method | Description | | -------------------------------- | ------ | ----------------------------------- | | `/api/sdk/payments/request` | POST | Request payment authorization | | `/api/sdk/payments/approve` | POST | Approve payment for external wallet | | `/api/sdk/payments/{id}/execute` | POST | Execute approved payment | | `/api/sdk/payments/{id}/confirm` | POST | Confirm external wallet payment | | `/api/sdk/payments/{id}` | GET | Get payment status | ## Error Handling | Status | Code | Description | | ------ | ----------------------- | ---------------------- | | 400 | `INVALID_INPUT` | Invalid request data | | 401 | `AUTH_FAILED` | Invalid SDK key | | 403 | `AGENT_SUSPENDED` | Agent is suspended | | 403 | `INSUFFICIENT_BALANCE` | Wallet balance too low | | 403 | `SPEND_LIMIT_EXCEEDED` | Spending limit reached | | 403 | `TIME_WINDOW_VIOLATION` | Outside allowed hours | | 429 | `RATE_LIMITED` | Too many requests | For full error handling patterns, see [Error Handling](https://conto.finance/docs/sdk/error-handling). ## Next Steps ### Make Your First Payment Link: https://conto.finance/docs/quickstart/first-payment Detailed payment flow walkthrough ### SDK Reference Link: https://conto.finance/docs/sdk/payments Full SDK documentation ### Examples Link: https://conto.finance/docs/sdk/examples Complete integration examples ### Error Handling Link: https://conto.finance/docs/sdk/error-handling Handle errors gracefully --- # Connect Agent & Wallet This guide walks through connecting your first agent and wallet on **Tempo Testnet**. By the end, you'll have an agent ready to make policy-enforced payments with free testnet tokens. ## Step 1: Create a Wallet Create a Tempo Testnet wallet to hold pathUSD tokens. Go to **Wallets** in the sidebar and click **Create Wallet**. | Field | Value | |-------|-------| | Name | Test Operations Wallet | | Custody Type | PRIVY (recommended) | | Chain Type | EVM | Click **Provision** to assign an onchain address on Tempo Testnet. Your wallet is now ready to receive pathUSD. Use the **Faucet** button on the wallet detail page to receive free testnet pathUSD tokens. ## Step 2: Connect an Agent Go to **Agents** in the sidebar and click **Connect Agent**. | Field | Description | Example | |-------|-------------|---------| | Name | Human-readable name | Test Payment Agent | | Type | Agent framework | CUSTOM (or your framework) | | Description | What the agent does | Quickstart test agent | Click **Connect Agent**. The agent starts in ACTIVE status. ### Via API ```bash curl -X POST https://conto.finance/api/agents \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Test Payment Agent", "agentType": "CUSTOM", "description": "Quickstart test agent" }' ``` ## Step 3: Link Wallet to Agent Connect the wallet to your agent with spending limits. Click on your agent to open its detail page. In the **Wallets** section, click **Assign Wallet** and select the Tempo Testnet wallet. Set limits for this agent-wallet link: | Setting | Recommended Value | Purpose | |---------|------------------|---------| | Per Transaction | `50` | Max per payment | | Daily Limit | `500` | Max per day | | Weekly Limit | `2000` | Max per week | | Monthly Limit | `5000` | Max per month | Do not set Per Transaction to `0` — this blocks all payments at the wallet level before policies are evaluated. Restrict when the agent can transact: ```json { "allowedHoursStart": 9, "allowedHoursEnd": 17, "allowedDays": ["Mon", "Tue", "Wed", "Thu", "Fri"] } ``` Defaults are unrestricted: hours 0-24, all 7 days. For testing, leave time windows at their defaults (24/7). ## Step 4: Generate an SDK Key On the agent detail page, click the **SDK Integration** tab. Click **Generate New Key** and enter a name (e.g., "Testnet Key"). Copy and save the key immediately — it's only shown once. Store it as an environment variable: ```bash export CONTO_API_KEY="conto_agent_your_key_here" ``` For scopes, expiration, and best practices, see the [Authentication guide](https://conto.finance/docs/sdk/authentication). ## Step 5: Verify Your Setup Confirm everything is connected: ```bash curl https://conto.finance/api/sdk/setup \ -H "Authorization: Bearer $CONTO_API_KEY" ``` Check that: - `agent.status` is `"ACTIVE"` - `wallets` array contains your Tempo Testnet wallet with a balance - `scopes` includes `payments:request` and `payments:execute` ## Agent Types | Type | Description | |------|-------------| | `OPENAI_ASSISTANT` | OpenAI Assistants API | | `ANTHROPIC_CLAUDE` | Claude via API | | `LANGCHAIN` | LangChain agents | | `AUTOGPT` | AutoGPT instances | | `CUSTOM` | Any other agent framework | ## Agent Status ### ACTIVE Agent can make transactions. Default status. ### PAUSED Temporarily disabled. Use for maintenance. ### SUSPENDED Suspended by admin. Requires manual review. ### REVOKED Permanently disabled. Cannot be reactivated. ## Next Steps ### Connect Your Agent Link: https://conto.finance/docs/quickstart/connecting-agents Framework-specific integration code ### Test Payments & Policies Link: https://conto.finance/docs/quickstart/first-payment Apply policies and run test transactions --- # Test Payments & Policies This guide walks through applying policies to your agent and running test transactions that verify they enforce correctly on **Tempo Testnet**. ## Prerequisites Agent created with an SDK key ([Step 1](https://conto.finance/docs/quickstart/first-agent)) Tempo Testnet wallet funded with pathUSD Wallet linked to agent with spending limits ## Step 1: Create Test Policies Create two policies to test different enforcement behaviors. ### Policy A: Spend Limit Go to **Policies** → **New Policy**. | Field | Value | |-------|-------| | Name | Test Spend Limit | | Description | Deny transactions over $15 | | Policy Type | SPEND_LIMIT | | Field | Value | |-------|-------| | Rule Type | MAX_AMOUNT | | Operator | LTE | | Value | 15 | | Action | ALLOW | This allows transactions up to $15. Anything above is denied. ### Policy B: Approval Threshold | Field | Value | |-------|-------| | Name | Test Approval Threshold | | Description | Require approval for transactions over $10 | | Policy Type | APPROVAL_THRESHOLD | | Field | Value | |-------|-------| | Rule Type | REQUIRE_APPROVAL_ABOVE | | Operator | GREATER_THAN | | Value | 10 | | Action | REQUIRE_APPROVAL | ## Step 2: Assign Policies to Agent 1. Open your agent's detail page 2. Go to the **Permissions** tab 3. Assign both "Test Spend Limit" and "Test Approval Threshold" All assigned policies are evaluated with AND logic — the most restrictive outcome wins. ## Step 3: Expected Behavior With both policies active: | Amount | Result | Why | |--------|--------|-----| | $5 | **APPROVED** | Under all thresholds | | $12 | **REQUIRES_APPROVAL** | Exceeds $10 approval threshold, under $15 limit | | $20 | **DENIED** | Exceeds $15 spend limit | ## Step 4: Run Test Transactions ### Test 1: $5 (Expect: APPROVED) ```bash curl -X POST https://conto.finance/api/sdk/payments/request \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 5, "recipientAddress": "0x1234567890abcdef1234567890abcdef12345678", "purpose": "Test - under all limits", "category": "TESTING" }' ``` Expected: `"status": "APPROVED"` with `"currency": "pathUSD"` ### Test 2: $12 (Expect: REQUIRES_APPROVAL) ```bash curl -X POST https://conto.finance/api/sdk/payments/request \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 12, "recipientAddress": "0x1234567890abcdef1234567890abcdef12345678", "purpose": "Test - over approval threshold" }' ``` Expected: `"status": "REQUIRES_APPROVAL"` with a violation message referencing the approval threshold. ### Test 3: $20 (Expect: DENIED) ```bash curl -X POST https://conto.finance/api/sdk/payments/request \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 20, "recipientAddress": "0x1234567890abcdef1234567890abcdef12345678", "purpose": "Test - over max amount" }' ``` Expected: `"status": "DENIED"` with a violation referencing the spend limit. ## Step 5: Execute an Approved Payment If Test 1 returned APPROVED, execute it on Tempo Testnet: ```bash curl -X POST https://conto.finance/api/sdk/payments/REQUEST_ID/execute \ -H "Authorization: Bearer $CONTO_API_KEY" ``` Replace `REQUEST_ID` with the `requestId` from the response. The response includes: - `txHash` — onchain transaction hash on Tempo Testnet - `explorerUrl` — link to view on [explore.tempo.xyz](https://explore.tempo.xyz) ## Step 6: Using the SDK The same tests using the TypeScript SDK: ```typescript import { Conto } from '@conto/sdk'; const conto = new Conto({ apiKey: process.env.CONTO_API_KEY }); // Test 1: Should approve const test1 = await conto.payments.request({ amount: 5, recipientAddress: '0x1234567890abcdef1234567890abcdef12345678', purpose: 'Test - under all limits', }); console.log('Test 1:', test1.status); // APPROVED // Execute the approved payment if (test1.status === 'APPROVED') { const result = await conto.payments.execute(test1.requestId); console.log('TX Hash:', result.txHash); console.log('Explorer:', result.explorerUrl); } // Test 2: Should require approval const test2 = await conto.payments.request({ amount: 12, recipientAddress: '0x1234567890abcdef1234567890abcdef12345678', purpose: 'Test - over approval threshold', }); console.log('Test 2:', test2.status); // REQUIRES_APPROVAL // Test 3: Should deny const test3 = await conto.payments.request({ amount: 20, recipientAddress: '0x1234567890abcdef1234567890abcdef12345678', purpose: 'Test - over max amount', }); console.log('Test 3:', test3.status); // DENIED ``` ## Step 7: Verify in Dashboard After executing a payment: 1. Go to **Transactions** in the dashboard 2. Find your transaction — it should show status "Confirmed" with Tempo Testnet chain 3. Click the explorer link to verify onchain 4. Check **Audit Logs** to see the full policy evaluation trail You've verified that policies enforce correctly and made a real onchain payment on Tempo Testnet. ## Troubleshooting The wallet-level per-transaction limit is set to `0`. Edit the wallet limits on the agent detail page and set it to a non-zero value. Policies use AND logic. If one policy denies while another requires approval, the denial wins. Check the **Permissions** tab for all assigned policies. Your testnet wallet needs funding. Use the **Faucet** button on the wallet detail page. Invalid or expired SDK key. Generate a new one from the agent detail page. ## Moving to Production Once you've verified policies work on testnet: 1. Create a production wallet — **Tempo Mainnet** (USDC.e, no gas token needed), **Base** (USDC), or **Solana** (USDC) 2. Fund it with real stablecoins 3. Link the production wallet to your agent with appropriate limits 4. Update or create production policies (the test policies can remain for reference) 5. Your SDK integration code stays the same ## Next Steps ### SDK Payments Reference Link: https://conto.finance/docs/sdk/payments Full SDK methods, parameters, and autoExecute ### Policy Types Link: https://conto.finance/docs/policies/overview Explore all policy rule types ### Error Handling Link: https://conto.finance/docs/sdk/error-handling Handle payment errors gracefully ### MCP Server Link: https://conto.finance/docs/mcp/overview Connect via Claude MCP for natural language payments --- # Quick Start Setup This guide walks you through setting up Conto end-to-end on **Tempo Testnet** — from account creation to a verified test payment with policy enforcement. No real funds required. ## What You'll Build By the end of this quickstart, you'll have: A Conto organization with your account An AI agent connected in Conto A testnet wallet funded with pathUSD Spending policies that approve, require approval, or deny payments A verified test payment on Tempo Testnet ## Why Tempo Testnet? We use **Tempo Testnet** for this quickstart because it requires **no gas token** — transactions are paid in pathUSD stablecoins, so there's no need to acquire ETH or SOL for gas fees. Combined with free faucet tokens, it's the fastest way to test the full Conto flow. | Property | Value | |----------|-------| | Network | Tempo Testnet | | Currency | pathUSD (TIP-20 stablecoin) | | Gas | No separate gas token needed | | Cost | Free (faucet tokens) | | Explorer | [explore.moderato.tempo.xyz](https://explore.moderato.tempo.xyz) | ## Step 1: Create Your Account 1. Visit [conto.finance](https://conto.finance) and sign up with email or Google OAuth 2. Create your **organization** — this is the top-level container for agents, wallets, and policies ## Step 2: Follow the Quickstart Pages ### 1. Connect Agent & Wallet Link: https://conto.finance/docs/quickstart/first-agent Connect an agent, create a Tempo Testnet wallet, link them with spending limits ### 2. Connect Your Agent Link: https://conto.finance/docs/quickstart/connecting-agents Framework-specific integration (OpenAI, Claude, LangChain, Python) ### 3. Test Payments Link: https://conto.finance/docs/quickstart/first-payment Apply policies and run test transactions that verify enforcement ## Moving to Production Once your test flow works on Tempo Testnet, switching to production is straightforward: 1. Create a production wallet — **Tempo Mainnet** (USDC.e, no gas token needed), **Base** (USDC), or **Solana** (USDC) 2. Fund it with real stablecoins 3. Link it to your agent with production spending limits Your policies, agents, and SDK integration code stay the same — only the wallet and chain change. Note: **Tempo Mainnet** is the easiest production option — like testnet, it requires no separate gas token. Transactions are paid in `USDC.e` directly. ## Alternative: CLI Quickstart If you prefer the command line, the [CLI quickstart](https://conto.finance/docs/cli/quickstart) lets you set up an agent with wallet, policies, and SDK key in one command: ```bash npx @conto_finance/create-conto-agent ``` ## Next Steps ### Connect Agent & Wallet Link: https://conto.finance/docs/quickstart/first-agent Start the quickstart ### Concepts Link: https://conto.finance/docs/introduction/concepts Understand agents, wallets, policies, and trust --- ## Global Options All commands support these flags: | Flag | Description | |------|-------------| | `--json` | Output in JSON format for scripting | | `--help` | Show help for any command | | `--version` | Show CLI version | ## Agent & Status ```bash conto status # Agent info, wallet balances, spending progress conto balance # Wallet balance table conto config # Show current configuration conto config # Update a config value ``` ## Payments ```bash conto pay
# Request and execute payment conto pay
--dry-run # Policy check without executing conto pay
--purpose "reason" # Add a purpose string conto pay
--wallet # Use a specific wallet conto pay
--category "API" # Tag with spend category ``` Info: `--dry-run` runs the full policy evaluation (all rules, all policies) and returns APPROVED, DENIED, or REQUIRES_APPROVAL without sending any transaction. ## Transactions ```bash conto transactions # List recent transactions (default: 10) conto transactions --limit 50 # List more transactions conto transactions # View transaction details ``` ## Wallets ```bash conto wallets # List all wallets with balances conto fund # Show chain-aware funding instructions ``` ## Spending Limits ```bash conto limits # Daily/weekly/monthly limits with progress bars ``` ## Alerts ```bash conto alerts # List all alerts conto alerts --unread # Unread alerts only ``` ## Policies See [Policy Management](https://conto.finance/docs/cli/policies) for full CRUD documentation. ```bash conto policies # List agent policies ([agent] vs [org-wide]) conto policies all # List all org policies conto policies create # Interactive policy builder conto policies update # Update name, priority, active status conto policies delete # Delete with confirmation conto policies assign # Assign policy to current agent conto policies unassign # Remove policy from agent conto policies add-rule # Add a rule to a policy conto policies remove-rule # Remove a rule ``` ## Authentication ```bash conto login # Re-authenticate (opens browser) conto login --base-url # Authenticate against a specific server conto logout # Clear stored credentials ``` ## MCP Server ```bash conto mcp # Start the Conto MCP server for Claude ``` ## Documentation ```bash conto docs # Open the CLI quickstart docs conto docs commands # Open the commands reference conto docs policies # Open the policy docs conto docs sdk # Open the SDK installation guide conto docs api # Open the public API reference ``` ## Init Wizard ```bash create-conto-agent # Run the setup wizard (interactive) conto init # Same as above # Non-interactive with flags: create-conto-agent --base-url https://conto.finance --name my-agent --type ANTHROPIC_CLAUDE --limit 500 --chain 4217 ``` ## JSON Mode Every command supports `--json` for machine-readable output: ```bash conto status --json | jq '.agent.name' conto pay --dry-run 0x... 10 --json | jq '.status' conto policies --json | jq '.[].name' ``` ## Configuration Files | File | Location | Purpose | |------|----------|---------| | `~/.conto/config.json` | Global | API keys, agent ID, server URL | | `.env.local` | Project | SDK key, chain config, wallet address | | `conto.config.json` | Project | Agent metadata (generated by init) | The CLI reads project-level `.env.local` first, then falls back to global `~/.conto/config.json`. --- ## Overview The CLI provides full CRUD for spending policies. When you list policies, each one is tagged as `[agent]` (directly assigned) or `[org-wide]` (inherited from your organization). ```bash conto policies ``` ## Create a Policy The interactive builder walks you through each step with descriptions: ```bash conto policies create ``` You'll be prompted for: 1. **Policy name** — A descriptive label 2. **Policy type** — 14 types available, each with an explanation 3. **Priority** — Lower numbers are evaluated first 4. **Rules** — Optionally add the first rule immediately 5. **Assignment** — Assign to the current agent or leave org-wide ## Policy Types | Type | Description | |------|-------------| | Spend Limit | Cap per-transaction, daily, weekly, or monthly totals | | Approval Threshold | Require human approval above a dollar amount | | Counterparty | Allow or block specific wallet addresses | | Category | Restrict which spend categories are permitted | | Geographic | Block transactions involving sanctioned countries | | Budget Allocation | Set a total budget cap for a time period | | Expiration | Policy only active within a date range | | Merchant | Allow or block specific merchants by address | | Time Window | Restrict transactions to specific hours or days | | Velocity | Rate limit: max transactions per time period | | Whitelist | Only pre-approved addresses can receive payments | | Composite | Combine multiple conditions with AND/OR logic | | Contract Allowlist | Restrict to approved smart contract addresses | | Blackout Period | Block all transactions during specific time windows | ## Add Rules Add rules to an existing policy: ```bash conto policies add-rule ``` The rule builder prompts for: - **Rule type** — Context-aware options based on the policy type - **Operator** — `<=`, `>=`, `in list`, `not in list`, `between`, etc. - **Value** — With smart placeholders (dollar amounts for limits, JSON arrays for lists, country codes for geographic rules) - **Action** — Allow, Deny, or Require Approval ### Value Examples by Rule Type | Rule Type | Example Value | Format | |-----------|--------------|--------| | Daily Limit | `500` | Dollar amount | | Allowed Counterparties | `["0xabc...","0xdef..."]` | JSON array of addresses | | Geographic Restriction | `["CU","IR","KP","SY"]` | JSON array of country codes | | Trust Score | `50` | Number (0-100) | | Date Range | `{"start":"2025-01-01","end":"2025-12-31"}` | JSON with ISO dates | | Velocity Limit | `5` | Max transactions per window | ## Update & Delete ```bash # Update name, priority, or active status conto policies update # Delete (with confirmation prompt) conto policies delete # Remove a specific rule from a policy conto policies remove-rule ``` ## Assign & Unassign Control which policies apply to your agent: ```bash # Assign an org policy to your agent conto policies assign # Remove a policy from your agent conto policies unassign ``` Org-wide policies (tagged `[org-wide]`) cannot be unassigned — they apply to all agents in the organization. To remove them, either delete the policy or set it to inactive with `conto policies update `. ## List All Org Policies To see every policy in the organization (not just your agent's): ```bash conto policies all ``` This shows the policy ID, agent count, rules, and active status for all policies. ## JSON Output All policy commands support `--json`: ```bash conto policies --json conto policies all --json conto policies create --json # Returns created policy as JSON ``` --- ## Install & Run ```bash npm npx @conto_finance/create-conto-agent ``` ```bash global install npm install -g @conto_finance/create-conto-agent create-conto-agent ``` The wizard provisions everything your agent needs: a wallet (Sponge custody), a spending policy, and an SDK key. No manual dashboard steps required. ## What Happens Choose your agent name, type, daily spending limit, and network. The CLI opens a browser window to `conto.finance/cli-auth`. Sign in with your Conto account (or create one). The browser redirects the token back to the CLI automatically. The wizard creates a wallet on your chosen chain, registers the agent, links the wallet with spend limits, creates a default spending policy, and generates an SDK key. Three files are written to your current directory: - `.env.local` — SDK key and configuration - `conto.config.json` — Agent metadata - `example.ts` — Working payment example ## Supported Networks | Network | Currency | Chain ID | Default | |---------|----------|----------|---------| | **Tempo** | USDC.e | 4217 | Yes | | Tempo Testnet | pathUSD | 42431 | — | | Base | USDC | 8453 | — | | Solana | USDC | mainnet-beta | — | Tempo is the default because it's stablecoin-native (no ETH needed), has sub-second finality, and negligible gas costs. Tempo Mainnet uses `USDC.e`; Tempo Testnet uses `pathUSD`. Info: On Tempo Testnet, the wizard automatically funds your wallet via faucet — no manual funding needed. ## Run the Example ```bash npx tsx example.ts ``` The example checks your agent status, wallet balance, and makes a test payment request through the policy engine. ## Next Steps After setup, use the `conto` CLI for day-to-day operations: ```bash conto status # Agent info and spending summary conto pay --dry-run 0x... 10 # Test a payment against policies conto policies # View assigned policies conto policies create # Create new policies interactively ``` ### Commands Reference Link: https://conto.finance/docs/cli/commands Full list of CLI commands and flags ### Policy Management Link: https://conto.finance/docs/cli/policies Create and manage spending policies from the CLI --- # Agent-to-Agent (A2A) Payments Conto supports direct payment requests between agents in the same organization. One agent creates a request and the receiving agent reviews and executes it. ## How It Works ```text Requesting agent -> creates request -> target agent reviews -> target agent executes ``` 1. Agent A calls `POST /api/sdk/a2a/request` 2. Agent B lists incoming requests via `GET /api/sdk/a2a/requests` 3. Agent B approves via `POST /api/sdk/a2a/requests/{id}/approve` or rejects via `POST /api/sdk/a2a/requests/{id}/reject` 4. Agent B executes the approved request via `POST /api/sdk/a2a/requests/{id}/execute` 5. The transfer still runs through Conto policy evaluation before settlement ## Create a Request You can target the receiving agent by `targetAgentId` or by `targetWalletAddress`. ```bash curl -X POST https://conto.finance/api/sdk/a2a/request \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "targetAgentId": "agent_xyz", "amount": 50, "currency": "USDC", "purpose": "Reimbursement for API credits", "metadata": { "invoiceId": "INV-2026-001" } }' ``` **Response** ```json { "success": true, "paymentRequest": { "id": "a2a_req_abc123", "status": "PENDING", "requestingAgentId": "agent_abc", "requestedFromAgentId": "agent_xyz", "amount": 50, "currency": "USDC", "purpose": "Reimbursement for API credits" }, "message": "Payment request sent to DevOps Agent" } ``` Info: If you pass `targetWalletAddress`, Conto resolves it to a registered agent first. Requests to unknown addresses are rejected. ## List Requests ```bash GET /api/sdk/a2a/requests ``` Use this endpoint to view both incoming and outgoing requests for the authenticated agent. ## Approve or Reject a Request The paying agent can explicitly approve a pending request before execution: ```bash curl -X POST https://conto.finance/api/sdk/a2a/requests/{id}/approve \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` To reject it, call the reject endpoint. You can include an optional `reason` that is stored with the request. ```bash curl -X POST https://conto.finance/api/sdk/a2a/requests/{id}/reject \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "reason": "This invoice is missing the deployment reference" }' ``` ## Execute a Request The receiving agent executes the request: ```bash curl -X POST https://conto.finance/api/sdk/a2a/requests/{id}/execute \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` Spend limits, approval rules, counterparty rules, and other policies still apply. ## Resolve an Agent from a Wallet Address ```bash curl -X POST https://conto.finance/api/sdk/a2a/resolve \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "address": "0x1234567890abcdef..." }' ``` ## A2A Statistics ```bash GET /api/sdk/a2a/stats ``` ## API Reference | Endpoint | Method | Description | | ------------------------------------ | ------ | -------------------------------------- | | `/api/sdk/a2a/request` | POST | Create an A2A payment request | | `/api/sdk/a2a/requests` | GET | List A2A requests | | `/api/sdk/a2a/requests/{id}` | GET | Get one request | | `/api/sdk/a2a/requests/{id}/approve` | POST | Approve a pending request | | `/api/sdk/a2a/requests/{id}/reject` | POST | Reject a pending request | | `/api/sdk/a2a/requests/{id}/execute` | POST | Execute an approved request | | `/api/sdk/a2a/resolve` | POST | Resolve an agent from a wallet address | | `/api/sdk/a2a/stats` | GET | View A2A statistics | --- # Admin SDK The Admin SDK lets you programmatically create agents, provision wallets, and configure spending policies. It uses **organization API keys** (`conto_xxx...`) which operate at the org level, separate from the agent SDK keys used for payments. ```typescript import { ContoAdmin } from '@conto/sdk'; const admin = new ContoAdmin({ orgApiKey: process.env.CONTO_ORG_API_KEY!, }); // Create a policy, create an agent, wire them together const policy = await admin.policies.create({ name: 'Daily $500 limit', policyType: 'SPEND_LIMIT', rules: [{ ruleType: 'DAILY_LIMIT', operator: 'LTE', value: '500' }], }); const agent = await admin.agents.create({ name: 'billing-agent', agentType: 'CUSTOM', }); await admin.agents.assignPolicy(agent.id, policy.id); ``` ## Organization API Keys Organization API keys authenticate at the org level and can manage all agents, wallets, and policies in the organization. They are different from agent SDK keys. | | Org API Key | Agent SDK Key | |---|---|---| | **Format** | `conto_xxx...` | `conto_agent_xxx...` | | **Scope** | Entire organization | Single agent | | **Used for** | Admin operations | Payment operations | | **SDK class** | `ContoAdmin` | `Conto` | ### Creating an Org API Key Navigate to **Settings** > **API Keys** in the dashboard. Click **Create API Key**, enter a name, and select a scope preset: - **Read Only** - View agents, wallets, policies - **Standard** - Read + write agents, wallets, policies - **Admin** - Full access (Owner only) Copy and save the key immediately. It is only shown once. ### Scopes Org API keys use fine-grained scopes that control what the key can access: | Scope | Description | |-------|-------------| | `agents:read` | List and view agents | | `agents:write` | Create, update, delete, freeze/unfreeze agents | | `wallets:read` | List and view wallets | | `wallets:write` | Create, update, delete, provision wallets | | `policies:read` | List and view policies and rules | | `policies:write` | Create, update, delete policies, manage rules, assign to agents | | `transactions:read` | View transaction history | | `counterparties:read` | View counterparties | | `counterparties:write` | Create and update counterparties | | `alerts:read` | View alerts | | `alerts:write` | Acknowledge and resolve alerts | | `analytics:read` | View spending analytics | | `audit:read` | View audit logs | ## Initialization ```typescript import { ContoAdmin } from '@conto/sdk'; const admin = new ContoAdmin({ orgApiKey: process.env.CONTO_ORG_API_KEY!, timeout: 30000, // Optional, 30s default }); ``` Info: `ContoAdmin` rejects agent SDK keys (`conto_agent_xxx`). If you pass one, it throws an error telling you to use the `Conto` class instead. ## admin.agents Manage the lifecycle of AI agents in your organization. ### agents.list() List agents with optional filters. ```typescript const { agents, total } = await admin.agents.list({ status: 'ACTIVE', search: 'billing', limit: 20, offset: 0, }); ``` | Parameter | Type | Description | |-----------|------|-------------| | `status` | string | Filter by status: ACTIVE, PAUSED, SUSPENDED, REVOKED, FROZEN | | `search` | string | Search by name or description | | `limit` | number | Results per page (1-100, default 50) | | `offset` | number | Pagination offset | ### agents.create() Create a new agent. ```typescript const agent = await admin.agents.create({ name: 'billing-agent', agentType: 'CUSTOM', description: 'Handles vendor payments', }); ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `name` | string | Yes | Agent name (1-100 chars) | | `agentType` | string | Yes | OPENAI_ASSISTANT, ANTHROPIC_CLAUDE, LANGCHAIN, AUTOGPT, or CUSTOM | | `description` | string | No | Description (max 500 chars) | | `publicKey` | string | No | Ethereum address (auto-generated if omitted) | | `externalId` | string | No | Your own identifier for the agent | ### agents.get() ```typescript const agent = await admin.agents.get('agent_id'); ``` ### agents.update() ```typescript await admin.agents.update('agent_id', { name: 'Updated Name', status: 'PAUSED', }); ``` ### agents.delete() Soft-deletes an agent. Deactivates its SDK keys and wallet/card links. ```typescript await admin.agents.delete('agent_id'); ``` ### agents.freeze() / agents.unfreeze() Freeze blocks all transactions for an agent. Unfreeze restores them. ```typescript await admin.agents.freeze('agent_id', { reason: 'Suspicious activity detected', freezeWallets: true, }); await admin.agents.unfreeze('agent_id', { reason: 'Investigation complete', unfreezeWallets: true, resetCounters: true, }); ``` ### agents.linkWallet() Link a wallet to an agent with spending limits. ```typescript await admin.agents.linkWallet('agent_id', { walletId: 'wallet_id', delegationType: 'LIMITED', spendLimitPerTx: 100, spendLimitDaily: 1000, spendLimitWeekly: 5000, allowedDays: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], }); ``` | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `walletId` | string | Required | Wallet to link | | `delegationType` | string | LIMITED | FULL, LIMITED, VIEW_ONLY, PREAPPROVED, ALLOWLIST | | `spendLimitPerTx` | number | 100 | Max per transaction | | `spendLimitDaily` | number | 1000 | Max per day | | `spendLimitWeekly` | number | - | Max per week | | `spendLimitMonthly` | number | - | Max per month | | `allowedHoursStart` | number | 0 | Allowed hours start (0-23) | | `allowedHoursEnd` | number | 24 | Allowed hours end (1-24) | | `allowedDays` | string[] | Mon-Fri | Allowed days of week | ### agents.assignPolicy() / agents.unassignPolicy() ```typescript await admin.agents.assignPolicy('agent_id', 'policy_id'); await admin.agents.unassignPolicy('agent_id', 'policy_id'); ``` ### agents.listWallets() / agents.listPolicies() ```typescript const { wallets } = await admin.agents.listWallets('agent_id'); const { policies } = await admin.agents.listPolicies('agent_id'); ``` ### agents.createSdkKey() / agents.listSdkKeys() / agents.revokeSdkKey() Create agent SDK keys programmatically. The key value is returned only once. ```typescript const { key } = await admin.agents.createSdkKey('agent_id', { name: 'Production Key', keyType: 'standard', expiresInDays: 90, }); console.log('Save this key:', key); // conto_agent_xxx... const keys = await admin.agents.listSdkKeys('agent_id'); await admin.agents.revokeSdkKey('agent_id', 'key_id'); ``` ## admin.wallets Manage wallets across the organization. ### wallets.list() ```typescript const { wallets, total } = await admin.wallets.list({ chainType: 'EVM', status: 'ACTIVE', limit: 20, }); ``` | Parameter | Type | Description | |-----------|------|-------------| | `status` | string | ACTIVE, FROZEN, ARCHIVED | | `chainType` | string | EVM or SOLANA | | `chainId` | string | Specific chain ID | | `limit` | number | Results per page (1-100, default 50) | | `offset` | number | Pagination offset | ### wallets.create() ```typescript const wallet = await admin.wallets.create({ name: 'billing-wallet', chainType: 'EVM', custodyType: 'SPONGE', }); ``` | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `name` | string | Required | Wallet name (1-100 chars) | | `chainType` | string | EVM | EVM or SOLANA | | `chainId` | string | - | Chain ID (defaults to Tempo testnet) | | `custodyType` | string | PRIVY | PRIVY, SPONGE, EXTERNAL, SMART_CONTRACT | | `walletType` | string | EOA | EOA, SMART_WALLET, MULTISIG | | `address` | string | - | Required when attaching an existing wallet instead of minting a new one | | `externalWalletId` | string | - | Existing provider wallet ID. Use with `custodyType=PRIVY` to attach an existing Privy wallet | | `importAddress` | string | - | Legacy watch-only import path for `EXTERNAL` custody | Attach an existing Privy-backed wallet: ```typescript const wallet = await admin.wallets.create({ name: 'enchant-treasury', chainType: 'EVM', chainId: '8453', custodyType: 'PRIVY', externalWalletId: 'privy_wallet_123', address: '0x1234...abcd', }); ``` Register a self-custodied wallet: ```typescript const wallet = await admin.wallets.create({ name: 'external-ops-wallet', chainType: 'EVM', chainId: '8453', custodyType: 'EXTERNAL', address: '0xabcd...1234', }); ``` `wallets.create()` is idempotent within the organization per `externalWalletId + chainId` or `address + chainId`, so retrying the same request returns the existing wallet instead of creating a duplicate. ### wallets.get() / wallets.update() / wallets.delete() ```typescript const wallet = await admin.wallets.get('wallet_id'); await admin.wallets.update('wallet_id', { name: 'Renamed Wallet', status: 'FROZEN', }); await admin.wallets.delete('wallet_id'); // Fails if linked to agents ``` Organization API keys can now manage the full wallet lifecycle through `wallets.get()`, `wallets.update()`, and `wallets.delete()` in addition to `wallets.list()` and `wallets.create()`. ### wallets.provision() Link a wallet to its custody provider and sync its onchain state. ```typescript const result = await admin.wallets.provision('wallet_id'); console.log('Address:', result.wallet.address); console.log('Balance:', result.wallet.balance, result.wallet.currency); ``` ### wallets.refreshBalance() Fetch the latest balance from the chain. ```typescript const result = await admin.wallets.refreshBalance('wallet_id'); console.log('Total:', result.totalBalance); ``` ## admin.policies Create and manage spending policies and their rules. ### policies.list() ```typescript const { policies, total } = await admin.policies.list({ limit: 50, offset: 0, }); ``` ### policies.create() Create a policy with optional rules and agent assignments in one call. ```typescript const policy = await admin.policies.create({ name: 'Conservative limits', policyType: 'SPEND_LIMIT', priority: 50, rules: [ { ruleType: 'MAX_AMOUNT', operator: 'LTE', value: '200' }, { ruleType: 'DAILY_LIMIT', operator: 'LTE', value: '1000' }, { ruleType: 'MONTHLY_LIMIT', operator: 'LTE', value: '10000' }, ], agentIds: ['agent_1', 'agent_2'], // Assign immediately }); ``` ### Policy Types | Type | Description | |------|-------------| | `SPEND_LIMIT` | Transaction and periodic spending limits | | `APPROVAL_THRESHOLD` | Require human approval above a threshold | | `COUNTERPARTY` | Allow/block specific counterparties | | `CATEGORY` | Allow/block spending categories | | `GEOGRAPHIC` | Country-based restrictions | | `TIME_WINDOW` | Time-of-day restrictions | | `VELOCITY` | Transaction frequency limits | | `WHITELIST` | Allowlist-only recipients | | `CONTRACT_ALLOWLIST` | Allowed smart contracts | | `BLACKOUT_PERIOD` | Block transactions during specific periods | | `BUDGET_ALLOCATION` | Budget cap enforcement | | `EXPIRATION` | Date-range validity | | `COMPOSITE` | Combine multiple rule types | | `MERCHANT` | Merchant category restrictions | ### policies.get() / policies.update() / policies.delete() ```typescript const policy = await admin.policies.get('policy_id'); await admin.policies.update('policy_id', { name: 'Updated name', isActive: false, priority: 75, }); await admin.policies.delete('policy_id'); ``` ### Rule Management Add, update, and remove individual rules on a policy. ```typescript // Add a single rule await admin.policies.addRule('policy_id', { ruleType: 'DAILY_LIMIT', operator: 'LTE', value: '500', action: 'DENY', }); // Add multiple rules at once await admin.policies.addRules('policy_id', [ { ruleType: 'MAX_AMOUNT', operator: 'LTE', value: '100' }, { ruleType: 'WEEKLY_LIMIT', operator: 'LTE', value: '2000' }, ]); // Update a rule await admin.policies.updateRule('policy_id', 'rule_id', { value: '750', }); // Delete a rule await admin.policies.deleteRule('policy_id', 'rule_id'); // Delete all rules await admin.policies.deleteAllRules('policy_id'); ``` ### Rule Operators | Operator | Description | |----------|-------------| | `EQUALS` | Exact match | | `NOT_EQUALS` | Not equal | | `GREATER_THAN` | Greater than | | `LESS_THAN` | Less than | | `GTE` | Greater than or equal | | `LTE` | Less than or equal | | `IN` / `IN_LIST` | Value is in list | | `NOT_IN` / `NOT_IN_LIST` | Value is not in list | | `BETWEEN` | Value is between two bounds | | `NOT_BETWEEN` | Value is outside bounds | | `DENY` | Always deny | ### Rule Actions | Action | Description | |--------|-------------| | `ALLOW` | Allow if condition met (default) | | `DENY` | Block if condition met | | `REQUIRE_APPROVAL` | Require human approval if condition met | ## Complete Example Provision a new agent from scratch: ```typescript import { ContoAdmin } from '@conto/sdk'; const admin = new ContoAdmin({ orgApiKey: process.env.CONTO_ORG_API_KEY!, }); // 1. Create a wallet const wallet = await admin.wallets.create({ name: 'ops-wallet', chainType: 'EVM', custodyType: 'SPONGE', }); await admin.wallets.provision(wallet.id); // 2. Create a spending policy const policy = await admin.policies.create({ name: 'Standard ops limits', policyType: 'SPEND_LIMIT', rules: [ { ruleType: 'MAX_AMOUNT', operator: 'LTE', value: '500' }, { ruleType: 'DAILY_LIMIT', operator: 'LTE', value: '2000' }, ], }); // 3. Create the agent const agent = await admin.agents.create({ name: 'ops-agent', agentType: 'CUSTOM', description: 'Handles operational payments', }); // 4. Link wallet with per-agent limits await admin.agents.linkWallet(agent.id, { walletId: wallet.id, delegationType: 'LIMITED', spendLimitPerTx: 500, spendLimitDaily: 2000, }); // 5. Assign the policy await admin.agents.assignPolicy(agent.id, policy.id); // 6. Generate an SDK key for the agent const { key } = await admin.agents.createSdkKey(agent.id, { name: 'Production', expiresInDays: 90, }); console.log('Agent ready. SDK key:', key); ``` ## Security Org API keys can manage all agents and wallets in the organization. Store them in a secrets manager (AWS Secrets Manager, Vault, etc.), never in source control. Use the minimum scope needed for the task. Create keys with only the scopes your pipeline needs. A deployment script that provisions agents only needs `agents:write`, `wallets:write`, and `policies:write`. Set expiration when creating keys. When rotating: 1. Create a new key 2. Update your secrets 3. Deploy 4. Revoke the old key Every operation made with an org API key is recorded in the audit log with actor type `API_KEY` and the key ID. Review audit logs in the dashboard under **Audit Logs**. ### Limitations Org API keys **cannot**: - Access the super admin panel (`/api/admin/*`) - Change billing plans - Rotate encryption keys - Manage feature flags These operations require dashboard session auth with an admin user account. ## Error Handling ```typescript try { await admin.agents.create({ name: 'test', agentType: 'CUSTOM' }); } catch (error) { console.error(error.code, error.status, error.message); } ``` | Error Code | Status | Description | |------------|--------|-------------| | `AUTH_FAILED` | 401 | Invalid or revoked API key | | `INSUFFICIENT_SCOPE` | 403 | Key lacks required scope | | `NOT_FOUND` | 404 | Resource not found | | `VALIDATION_FAILED` | 400 | Invalid request body | | `TIMEOUT` | 0 | Request timed out | ## Next Steps ### Policies Link: https://conto.finance/docs/policies/overview Learn about the policy engine and all rule types ### Authentication Link: https://conto.finance/docs/sdk/authentication Agent SDK keys and scopes ### Payments Link: https://conto.finance/docs/sdk/payments Making payments with agent SDK keys ### CLI Policies Link: https://conto.finance/docs/cli/policies Manage policies from the command line --- # SDK Authentication Conto SDK requests authenticate with agent-specific SDK keys: ```text conto_agent_[64-character-hex-string] ``` Every SDK key belongs to exactly one agent. SDK keys always expire automatically: the default lifetime is **365 days** and the maximum is **730 days**. ## Choose the Right Credential Use the credential type that matches the job you need to do: | Credential | Format | Best for | Notes | | ------------------------ | ----------------- | ----------------------------------------------------------------------------------- | --------------------------------------------------- | | **Standard SDK key** | `conto_agent_...` | Agent payment requests and agent-scoped read access | Works with the `Conto` client | | **Admin SDK key** | `conto_agent_...` | Delegated agent workflows that need elevated access to agents, wallets, or policies | Agent-scoped identity with an expanded scope preset | | **Organization API key** | `conto_...` | Backend/admin automation across the whole organization | Use with `ContoAdmin` | Info: `ContoAdmin` requires an **organization API key**. Admin SDK keys can call elevated HTTP API endpoints, but they are not a drop-in replacement for the `ContoAdmin` constructor. ## Generate SDK Keys ### Via Dashboard Go to **Agents** and select the agent that will use the key. Open **SDK Keys** and click **Generate New Key**. Select **Standard** for least-privilege access or **Admin** if the agent needs elevated management access. Choose an expiration window. Keys default to 365 days and cannot exceed 730 days. The full key is shown only once. Store it in your secrets manager before closing the dialog. ### Via API ```bash curl -X POST https://conto.finance/api/agents/{agentId}/sdk-keys \ -H "Authorization: Bearer $CONTO_ORG_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Production Key", "expiresInDays": 90, "keyType": "standard", "scopes": ["payments:request", "payments:execute", "wallets:read"] }' ``` **Response** ```json { "id": "key_abc123", "key": "conto_agent_abc123def456...", "keyPrefix": "conto_agent_abc...", "name": "Production Key", "keyType": "standard", "expiresAt": "2026-07-15T10:00:00Z", "message": "Save this key now! It will not be shown again." } ``` ## Use SDK Keys ### Initialize the agent SDK ```typescript import { Conto } from '@conto/sdk'; const conto = new Conto({ apiKey: process.env.CONTO_API_KEY!, }); ``` ### Use environment variables ```bash .env CONTO_API_KEY=conto_agent_abc123def456... ``` ### Use org keys with `ContoAdmin` ```typescript import { ContoAdmin } from '@conto/sdk'; const admin = new ContoAdmin({ orgApiKey: process.env.CONTO_ORG_API_KEY!, }); ``` ### Admin SDK Reference Link: https://conto.finance/docs/sdk/admin Use organization API keys with `ContoAdmin` for organization-wide provisioning and management. Info: Organization API keys are the right credential for programmatic wallet provisioning and cleanup. That includes `create`, `get`, `update`, `archive`, and `delete` wallet operations through the Admin SDK or the corresponding `/api/wallets` HTTP endpoints. ## Standard SDK Scopes If you omit `scopes`, standard SDK keys get the default least-privilege preset: | Scope | Included by default | Description | | ---------------------- | ------------------- | --------------------------------------------------------- | | `payments:request` | Yes | Request policy evaluation for a payment | | `wallets:read` | Yes | View wallet balances and limits | | `policies:read` | Yes | View policies assigned to the agent | | `transactions:read` | Yes | View transaction history | | `counterparties:read` | Yes | View counterparties and trust data | | `alerts:read` | Yes | View alerts related to the agent | | `agents:read` | Yes | View agent profile and setup summary | | `analytics:read` | Yes | View spend analytics | | `network:read` | Yes | Query network trust data | | `payments:execute` | No | Execute approved payments or use `autoExecute` | | `payments:approve` | No | Approve external-wallet payments | | `payments:confirm` | No | Confirm external-wallet payments | | `transactions:write` | No | Retry failed transactions or record x402/MPP transactions | | `policies:exceptions` | No | Request and view policy exceptions | | `counterparties:write` | No | Create and update counterparties | | `alerts:write` | No | Acknowledge and resolve alerts | | `audit:read` | No | View audit logs | Info: Use the `scopes` array when creating a **standard** SDK key via API if the agent needs more than the default preset. ## Admin SDK Keys Admin SDK keys use an elevated preset intended for delegated agent management workflows. They include: - All standard SDK scopes - `agents:write` - `wallets:write` - `policies:write` They do **not** include organization-superuser capabilities such as team management, organization settings, or the `admin` super-scope. They also cannot create other admin SDK keys. That escalation path is blocked intentionally. ## Key Expiration All SDK keys have a mandatory expiration. | Value | Behavior | | ------- | -------------------------------------- | | Omitted | Defaults to **365 days** | | `30` | Short-lived testing key | | `90` | Recommended production rotation window | | `365` | Long-lived standard key | | `730` | Maximum allowed lifetime | There is no non-expiring SDK key mode. Build key rotation into your operational runbooks. ## Revoke Keys ### Via Dashboard 1. Go to **Agents** 2. Open the agent 3. Open **SDK Keys** 4. Click **Revoke** ### Via API ```bash curl -X DELETE "https://conto.finance/api/agents/{agentId}/sdk-keys?keyId={keyId}" \ -H "Authorization: Bearer $CONTO_ORG_API_KEY" ``` Revocation is immediate. ## Best Practices - Store SDK keys in a secrets manager, not in source control. - Use separate keys for development, staging, and production. - Prefer standard keys unless the agent truly needs elevated management access. - Grant `payments:execute` only to agents that should actually move funds. - Rotate keys on a schedule instead of waiting for emergency revocations. --- # Budget Requests Budget requests let an agent ask for a spending budget that a human reviews and approves in the dashboard. Once approved, spend is tracked against the approved amount across **all payment types** (standard, x402, MPP, card). When the budget is exhausted, further payments are blocked. Info: Budget requests are **optional**. If no active budget exists for an agent, all payment flows work normally using existing spend limits and policies. When an active budget _does_ exist, both the budget and policies must pass for a payment to go through. ## How It Works ``` Agent Human (Dashboard) │ │ ├─ request_budget ──────────────────────> │ Sees request in Budgets tab │ (amount, purpose) │ │ │ ├─ get_budget_request ◄────────────────── │ Approves (optionally adjusts │ (polls for APPROVED) │ amount) or rejects │ │ ├─ Makes payments normally: │ │ • payments/request ──> budget check │ │ • x402/pre-authorize ──> budget check │ │ • mpp/pre-authorize ──> budget check │ │ • After execution ──> spend decremented │ │ │ └─ Budget exhausted → payments blocked │ Sees spend progress bar ``` ## Lifecycle A budget request moves through these states: | Status | Description | |--------|-------------| | `PENDING` | Awaiting human review. Expires 24h after creation if not acted on. | | `APPROVED` | Human approved. Spend is tracked. Expires at a set time (default 24h after approval). | | `REJECTED` | Human rejected with an optional reason. | | `EXPIRED` | Timed out without approval, or approved budget window elapsed. | | `EXHAUSTED` | Approved budget fully spent (`currentSpend >= approvedAmount`). | **Constraints:** - One active (PENDING or APPROVED) budget request per agent at a time - Creating a new request while one is active returns `409 BUDGET_REQUEST_EXISTS` ## SDK API ### Create a Budget Request ```bash curl -X POST https://conto.finance/api/sdk/budget-request \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 50, "purpose": "Research 200 companies via StableEnrich and generate reports", "category": "API_PROVIDER" }' ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `amount` | number | Yes | Requested budget in USDC | | `purpose` | string | Yes | What this budget is for (shown to the reviewer) | | `category` | string | No | Spend category (e.g., `API_PROVIDER`, `INFRASTRUCTURE`) | | `metadata` | object | No | Arbitrary context (JSON) | **Response (201):** ```json { "budgetRequestId": "cmm59z...", "status": "PENDING", "requestedAmount": 50.00, "purpose": "Research 200 companies via StableEnrich and generate reports", "category": "API_PROVIDER", "createdAt": "2026-04-07T12:00:00.000Z" } ``` **Error (409) — active request already exists:** ```json { "error": "Agent already has an active budget request", "code": "BUDGET_REQUEST_EXISTS", "existingBudgetRequestId": "cmm58y..." } ``` ### Check Budget Request Status ```bash curl https://conto.finance/api/sdk/budget-request?status=APPROVED \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` **Query Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `id` | string | No | Specific budget request ID | | `status` | string | No | Filter: `PENDING`, `APPROVED`, `REJECTED`, `EXPIRED`, `EXHAUSTED` | **Response (200):** ```json { "budgetRequests": [ { "id": "cmm59z...", "status": "APPROVED", "requestedAmount": 50.00, "approvedAmount": 25.00, "currentSpend": 12.50, "remainingBudget": 12.50, "purpose": "Research 200 companies via StableEnrich and generate reports", "category": "API_PROVIDER", "expiresAt": "2026-04-08T12:00:00.000Z", "createdAt": "2026-04-07T12:00:00.000Z" } ] } ``` ## MCP Tools ### request_budget Request a spending budget for payments. Submits a request for human approval. Only one active budget request per agent at a time. Applies to all payment types. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `amount` | number | Yes | Requested budget in USDC | | `purpose` | string | Yes | What this budget is for (shown to reviewer) | | `category` | string | No | Spend category (e.g., `API_PROVIDER`, `INFRASTRUCTURE`) | | `metadata` | object | No | Additional context | ### get_budget_request Check status and remaining balance of your budget requests. Use to poll for approval and monitor remaining budget. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `budgetRequestId` | string | No | Specific request ID | | `status` | string | No | Filter: `PENDING`, `APPROVED`, `REJECTED`, `EXPIRED`, `EXHAUSTED` | ## Payment Flow Integration When an active (approved, non-expired) budget exists, every payment flow checks it automatically: **Pre-execution (blocking):** - `payments/request` — checks budget before policy evaluation - `payments/approve` — checks budget for external wallet payments - `x402/pre-authorize` — checks budget before authorizing x402 payments - `mpp/pre-authorize` — checks budget before authorizing MPP payments **Post-execution (decrement):** - `payments/execute` — decrements budget after successful execution - `x402/record` — decrements budget when recording x402 payments - `mpp/record` — decrements budget when recording MPP payments If a payment would exceed the remaining budget, it is denied with a `budgetRequest` object in the response: ```json { "authorized": false, "reasons": ["Payment of $30.00 exceeds remaining budget of $12.50"], "budgetRequest": { "id": "cmm59z...", "approvedAmount": 25.00, "currentSpend": 12.50, "remainingBudget": 12.50 } } ``` ## Dashboard Budget requests appear in the **Budgets** tab on the Alerts & Approvals page. **Pending requests** show: - Agent name, purpose, requested amount, category - Approve / Reject buttons - Optional amount adjustment field (approve a different amount than requested) **Approved requests** show: - Spend progress bar (currentSpend / approvedAmount) - Remaining balance and expiry countdown - Whether the amount was adjusted from the original request **Completed requests** (rejected, expired, exhausted) show their final state. ### Approving a Request ```bash curl -X POST https://conto.finance/api/budget-requests/{id}/decide \ -H "Authorization: Bearer $SESSION_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "decision": "APPROVED", "approvedAmount": 25.00, "expiresInHours": 48 }' ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `decision` | string | Yes | `APPROVED` or `REJECTED` | | `approvedAmount` | number | No | Override amount (defaults to requested amount) | | `expiresInHours` | number | No | Budget window in hours (default: 24) | | `comment` | string | No | Optional note | ## Audit Trail Every state transition creates an audit log entry: | Transition | Action | Actor | |------------|--------|-------| | Agent creates request | `CREATE` | Agent | | Human approves | `APPROVE` | User | | Human rejects | `REJECT` | User | | Spend recorded | `UPDATE` | Agent | | Budget exhausted | `UPDATE` | System | | Budget expired | `UPDATE` | System | ## Example: Agent Workflow ```typescript // 1. Request a budget const budget = await fetch('/api/sdk/budget-request', { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ amount: 50, purpose: 'Research 200 companies via paid APIs', category: 'API_PROVIDER', }), }).then(r => r.json()); console.log('Budget request:', budget.budgetRequestId); // PENDING // 2. Poll for approval let approved = false; while (!approved) { const status = await fetch( `/api/sdk/budget-request?id=${budget.budgetRequestId}`, { headers: { 'Authorization': `Bearer ${apiKey}` } } ).then(r => r.json()); const req = status.budgetRequests[0]; if (req.status === 'APPROVED') { console.log(`Approved: $${req.approvedAmount} budget`); approved = true; } else if (req.status === 'REJECTED' || req.status === 'EXPIRED') { throw new Error(`Budget ${req.status}`); } await new Promise(r => setTimeout(r, 5000)); } // 3. Make payments normally — budget is enforced automatically await conto.payments.pay({ amount: 0.05, recipientAddress: '0x...', purpose: 'StableEnrich API call', }); // Budget decremented: $49.95 remaining ``` ## Next Steps ### Payments Link: https://conto.finance/docs/sdk/payments Standard payment request and execution ### x402 Payments Link: https://conto.finance/docs/sdk/x402-payments HTTP 402 micropayment handling ### Spend Limits Link: https://conto.finance/docs/policies/spend-limits Configure wallet spend limits ### MCP Tools Link: https://conto.finance/docs/mcp/tools Full MCP tools reference --- # Card Payments Conto supports connecting existing payment cards to the policy engine, giving you spend limits, merchant controls, time windows, and MCC-based restrictions for agent card usage. Info: **Stripe Issuing** and **Lithic** card provisioning (issuing new cards directly through Conto) is coming soon. Currently, you can connect any existing card manually and enforce policies through the SDK approve/confirm flow. ## Connecting a Card Register an existing card by providing its last 4 digits, brand, and spend limits: ```bash curl -X POST https://conto.finance/api/cards \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "provider": "MANUAL", "name": "Marketing Card", "last4": "4242", "brand": "VISA", "cardType": "VIRTUAL", "spendLimitDaily": 1000, "spendLimitPerTx": 500 }' ``` **Response:** ```json { "card": { "id": "clx_abc123", "name": "Marketing Card", "provider": "MANUAL", "last4": "4242", "brand": "VISA", "status": "ACTIVE" } } ``` You can also create cards from the dashboard at **Cards > Create Card**. ## Assigning Cards to Agents After connecting a card, assign it to an agent with per-agent limits: ```bash curl -X POST https://conto.finance/api/agents/{agentId}/cards \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "cardId": "clx_abc123", "spendLimitPerTx": 250, "spendLimitDaily": 500, "allowedDays": ["Mon", "Tue", "Wed", "Thu", "Fri"], "allowedHoursStart": 9, "allowedHoursEnd": 17, "blockedCategories": ["7995"] }' ``` Agent-level limits cannot exceed card-level limits. One card can be assigned to multiple agents, each with their own spend controls. ## Card State Management Pause, resume, or cancel cards from the dashboard or API: ```bash # Pause a card curl -X PATCH https://conto.finance/api/cards/{cardId}/state \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{ "state": "PAUSED" }' # Resume a card curl -X PATCH https://conto.finance/api/cards/{cardId}/state \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{ "state": "ACTIVE" }' # Cancel a card (deactivates all agent links) curl -X PATCH https://conto.finance/api/cards/{cardId}/state \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{ "state": "CANCELLED" }' ``` ## Linking Policies to Cards Beyond the per-agent field-based limits, you can link named policies to cards. This uses the same `Policy` / `PolicyRule` framework as wallets and agents. ```bash # Link a policy to a card curl -X POST https://conto.finance/api/cards/{cardId}/policies \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{ "policyId": "policy_abc123" }' # List linked policies curl https://conto.finance/api/cards/{cardId}/policies \ -H "Authorization: Bearer $CONTO_API_KEY" # Unlink a policy curl -X DELETE https://conto.finance/api/cards/{cardId}/policies/{policyId} \ -H "Authorization: Bearer $CONTO_API_KEY" ``` Card policies support all standard rule types plus card-specific ones: | Rule Type | Description | |-----------|-------------| | `CARD_ALLOWED_MCCS` | Whitelist merchant category codes | | `CARD_BLOCKED_MCCS` | Blocklist merchant category codes | | `CARD_ALLOWED_MERCHANTS` | Whitelist merchant names/IDs | | `CARD_BLOCKED_MERCHANTS` | Blocklist merchant names/IDs | | `CARD_MAX_AMOUNT` | Per-card transaction amount ceiling | See [Advanced Policies](https://conto.finance/docs/policies/advanced#card-payment-rules) for value formats. ## Policy Enforcement Card payments are evaluated through two layers: ### Layer 1: Agent-Card Limits Field-based limits set when assigning a card to an agent: - Per-transaction, daily, weekly, monthly spend limits - Time windows (allowed days and hours) - MCC category allow/block lists - Merchant allow/block lists ### Layer 2: Policy Rule Engine Database-defined policies linked to the card or agent: - All standard rule types (velocity, geographic, approval thresholds) - Card-specific MCC and merchant rules - Priority-ordered evaluation with AND logic Both layers run together. The first violation from either layer denies the payment. ## SDK Approve/Confirm Flow Agents use the approve/confirm pattern before charging a connected card: Agent calls `POST /api/sdk/cards/approve` before charging the card. ```bash curl -X POST https://conto.finance/api/sdk/cards/approve \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "cardId": "clx_abc123", "amount": 99.99, "merchantName": "AWS", "merchantCategory": "5734", "purpose": "EC2 instance charges" }' ``` Conto evaluates both policy layers and returns an approval token (valid 5 minutes). ```json { "approved": true, "requestId": "cpr_xyz", "approvalToken": "a1b2c3...", "expiresAt": "2025-06-15T10:05:00Z", "card": { "last4": "4242", "brand": "VISA" }, "limits": { "remainingDaily": 400.01, "remainingPerTx": 500 } } ``` Agent processes the card charge through its own payment integration. Agent calls `POST /api/sdk/cards/{requestId}/confirm` with the transaction reference. ```bash curl -X POST https://conto.finance/api/sdk/cards/cpr_xyz/confirm \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "approvalToken": "a1b2c3...", "externalTxId": "ch_1234567890", "authorizationCode": "A1B2C3", "actualAmount": 99.99 }' ``` You can also include `merchantDetails` if the card processor returns the settled merchant name, MCC, or location after authorization. ## Webhook Authorizations (Coming Soon) Webhook-based real-time authorization requires Stripe Issuing or Lithic integration, which is coming soon. The webhook handlers are built and ready for when provider integration is enabled. When Stripe Issuing or Lithic integration is available, real-time authorization webhooks will provide automatic policy enforcement without the SDK approve/confirm flow. The card provider sends an authorization request to Conto, which evaluates policies and responds with approve or decline within 2 seconds. | Provider | Webhook Endpoint | Secret Env Var | |----------|-----------------|----------------| | Stripe Issuing | `/api/webhooks/cards/stripe` | `STRIPE_ISSUING_WEBHOOK_SECRET` | | Lithic | `/api/webhooks/cards/lithic` | `LITHIC_WEBHOOK_SECRET` | | Privacy.com | `/api/webhooks/cards/privacy` | `PRIVACY_WEBHOOK_SECRET` | ## API Reference | Endpoint | Method | Description | |----------|--------|-------------| | `/api/cards` | GET | List all cards | | `/api/cards` | POST | Connect a card (manual) | | `/api/cards/{id}` | GET | Card details | | `/api/cards/{id}` | PATCH | Update card limits/settings | | `/api/cards/{id}/state` | PATCH | Change card state | | `/api/cards/{id}/policies` | GET | List linked policies | | `/api/cards/{id}/policies` | POST | Link a policy | | `/api/cards/{id}/policies/{policyId}` | DELETE | Unlink a policy | | `/api/agents/{id}/cards` | GET | List agent's cards | | `/api/agents/{id}/cards` | POST | Assign card to agent | | `/api/sdk/cards/approve` | POST | SDK: request card approval | | `/api/sdk/cards/{id}/confirm` | POST | SDK: confirm card payment | ## Roadmap | Feature | Status | |---------|--------| | Manual card connection + policy enforcement | Available | | SDK approve/confirm flow | Available | | Card policy linking (CardPolicy) | Available | | Stripe Issuing card provisioning | Coming soon | | Lithic card provisioning | Coming soon | | Provider card import | Coming soon | | Real-time webhook authorizations | Coming soon | ## Card Alert Types Card transactions are monitored for anomalies after each confirmed payment. These alerts are created automatically: | Alert Type | Severity | Trigger | |---|---|---| | `CARD_SPEND_VELOCITY` | MEDIUM/HIGH | Current hour spend exceeds 3x the 30-day hourly average | | `CARD_LARGE_TX` | MEDIUM/HIGH | Single transaction exceeds 3x the 30-day per-transaction average. HIGH if ratio exceeds 10x | | `CARD_NEW_MERCHANT` | LOW | First transaction with a given merchant | | `CARD_RAPID_SWITCHING` | MEDIUM/HIGH | More than 5 distinct merchants within 60 minutes. HIGH if more than 10 | | `CARD_DAILY_BURN` | MEDIUM/HIGH | Daily spend exceeds 80% of daily limit. HIGH if exceeds 95% | Info: Card alerts require sufficient transaction history for statistical detection. `CARD_SPEND_VELOCITY` needs at least 5 days of history and 10 prior transactions. `CARD_LARGE_TX` needs at least 10 prior transactions. ## Next Steps ### Card Management Guide Link: https://conto.finance/docs/guides/card-management Dashboard walkthrough for managing cards ### Advanced Policies Link: https://conto.finance/docs/policies/advanced#card-payment-rules Card-specific policy rule types ### Standard Payments Link: https://conto.finance/docs/sdk/payments Stablecoin payment flow ### Spend Limits Link: https://conto.finance/docs/policies/spend-limits Configure spending controls --- # Error Handling The Conto SDK provides detailed error information to help you handle failures gracefully. ## ContoError Type All SDK errors include `code` and `status` properties alongside the standard `Error` fields: ```typescript interface ContoError extends Error { code: string; // Error code (e.g., 'PAYMENT_DENIED') status: number; // HTTP status code message: string; // Human-readable message } ``` `ContoError` is a TypeScript interface, not a class. The SDK attaches `code` and `status` to standard `Error` objects. Use property checks (`'code' in error`) instead of `instanceof` to detect SDK errors. ## Error Codes ### Authentication Errors | Code | Status | Description | Solution | |------|--------|-------------|----------| | `AUTH_FAILED` | 401 | Invalid or expired API key | Check key is correct and not revoked | | `EXPIRED_KEY` | 401 | Key has expired | Generate a new SDK key | | `INSUFFICIENT_SCOPE` | 403 | Key lacks required permission | Use key with required scope | ### Payment Errors | Code | Status | Description | Solution | |------|--------|-------------|----------| | `PAYMENT_DENIED` | 403 | Payment blocked by policy | Check violations for details | | `REQUIRES_APPROVAL` | 202 | Needs manual approval | Wait for human approval | | `INSUFFICIENT_BALANCE` | 400 | Wallet has insufficient funds | Fund the wallet | | `DAILY_LIMIT_EXCEEDED` | 400 | Daily limit exceeded | Wait for reset or increase limit | | `PER_TX_LIMIT_EXCEEDED` | 400 | Amount exceeds per-tx limit | Reduce amount or increase limit | | `EXPIRED` | 400 | Payment request expired | Request new approval | | `NOT_FOUND` | 404 | Request ID not found | Check the request ID | | `INVALID_STATUS` | 400 | Cannot execute in current status | Check payment status | | `NO_WALLET` | 400 | No wallet assigned | Link a wallet to the agent | | `EXTERNAL_WALLET` | 400 | Cannot execute external wallet via /execute | Use /confirm with your own txHash | | `POLICY_DENIED` | 400 | Payment denied by policy (enriched) | Check context.nextSteps | | `EXTERNAL_WALLET_ERROR` | 400 | No eligible wallet with balance for this payment. The response includes `alternatives` showing other wallets. | | `WALLET_CONFIG_ERROR` | 400 | Wallet is misconfigured (e.g., missing custody provider). Contact your admin. | | `CUSTODY_NOT_CONFIGURED` | 500 | The custody provider (Sponge or Privy) is not configured on the server. | | `MANUAL_EXECUTION_REQUIRED` | 400 | External or smart contract wallets cannot be auto-executed. Use the approve/confirm flow instead. | | `REQUEST_EXPIRED` | 400 | The payment request expired before execution. Re-request. | | `ALREADY_EXECUTED` | 400 | This payment request was already executed. Check transaction status instead. | ### Validation Errors | Code | Status | Description | Solution | |------|--------|-------------|----------| | `VALIDATION_ERROR` | 400 | Invalid request body | Check request parameters | | `INVALID_AMOUNT` | 400 | Amount must be positive | Use a positive number | | `INVALID_ADDRESS` | 400 | Malformed wallet address | Use valid 0x address (EVM) or base58 address (Solana) | | `INVALID_JSON` | 400 | Malformed request body | Send valid JSON | ### System Errors | Code | Status | Description | Solution | |------|--------|-------------|----------| | `RATE_LIMITED` | 429 | Too many requests | Wait and retry | | `TIMEOUT` | 0 | Request timeout | Retry with longer timeout | | `INTERNAL_ERROR` | 500 | Server error | Retry or contact support | ## Handling Errors ### Basic Error Handling ```typescript import { Conto } from '@conto/sdk'; import type { ContoError } from '@conto/sdk'; try { const result = await conto.payments.pay({ amount: 100, recipientAddress: '0x...' }); } catch (error) { if (error instanceof Error && 'code' in error) { const sdkError = error as ContoError; console.error(`Error [${sdkError.code}]:`, sdkError.message); console.error('HTTP Status:', sdkError.status); } else { console.error('Unexpected error:', error); } } ``` ### Handling Specific Error Codes ```typescript try { await conto.payments.pay({ ... }); } catch (error) { if (error instanceof Error && 'code' in error) { switch (error.code) { case 'PAYMENT_DENIED': console.log('Payment was denied by policy'); // Check why it was denied break; case 'REQUIRES_APPROVAL': console.log('Payment needs human approval'); // Notify approvers break; case 'INSUFFICIENT_BALANCE': console.log('Wallet needs funding'); // Alert treasury team break; case 'DAILY_LIMIT_EXCEEDED': console.log('Daily limit reached'); // Wait until tomorrow or increase limit break; case 'RATE_LIMITED': console.log('Too many requests, waiting...'); // Check retryAfter header break; default: console.error('Unhandled error:', error.code); } } } ``` ## Enriched Error Responses Some SDK error responses include additional context to help agents recover programmatically. These enriched errors include `hint`, `context`, and `nextSteps` fields. ### Error Response Structure ```typescript interface EnrichedError { error: string; // Human-readable message code: string; // Machine-readable code hint?: string; // Actionable suggestion details?: object; // Violation details (for POLICY_DENIED) context?: { wallets?: Array<{ // Available wallet balances id: string; address: string; chainId: string; custodyType: string; balance: number; }>; alternatives?: Array<{ // Executable wallet alternatives (for EXTERNAL_WALLET) walletId: string; address: string; custodyType: string; chainId: string; balance: number; reason: string; }>; nextSteps?: string[]; // Suggested recovery actions }; } ``` ### Example: External Wallet Error When trying to `/execute` a payment assigned to an external wallet: ```json { "error": "External wallets cannot use /execute. Use /confirm with your own transaction hash instead.", "code": "EXTERNAL_WALLET", "hint": "Executable wallets are available. Re-submit the payment request with a specific walletId, or use /confirm for external wallets.", "context": { "alternatives": [ { "walletId": "wal_abc123", "address": "0x...", "custodyType": "PRIVY", "chainId": "42431", "balance": 500.00, "reason": "PRIVY wallet with sufficient balance" } ], "nextSteps": [ "Re-request payment with walletId of an executable wallet", "Or execute externally and call POST /api/sdk/payments/{id}/confirm with txHash" ] } } ``` ### Example: Insufficient Balance Error ```json { "error": "Insufficient wallet balance", "code": "INSUFFICIENT_BALANCE", "hint": "Requested $100 but max available balance is $45. Try a smaller amount or fund the wallet.", "context": { "nextSteps": [ "Reduce the payment amount", "Fund the wallet with more stablecoins", "Use GET /api/sdk/setup to check current balances" ] } } ``` ### Handling Enriched Errors ```typescript try { await fetch(`/api/sdk/payments/${id}/execute`, { method: 'POST', ... }); } catch (error) { const body = await error.json(); if (body.code === 'EXTERNAL_WALLET' && body.context?.alternatives?.length) { // Re-request with an executable wallet const alt = body.context.alternatives[0]; console.log(`Retrying with ${alt.custodyType} wallet: ${alt.address}`); } if (body.context?.nextSteps) { console.log('Suggested actions:', body.context.nextSteps); } } ``` ### Using Request/Execute for Better Control The `pay()` method throws on denial. For more control, use separate request/execute: ```typescript // Request first (never throws for denied payments) const request = await conto.payments.request({ amount: 100, recipientAddress: '0x...' }); if (request.status === 'DENIED') { console.log('Denied reasons:', request.reasons); console.log('Violations:', request.violations); return null; } if (request.status === 'REQUIRES_APPROVAL') { console.log('Awaiting approval...'); return { pending: true, requestId: request.requestId }; } // Only execute if approved try { return await conto.payments.execute(request.requestId); } catch (error) { // Handle execution errors if (error.code === 'INSUFFICIENT_BALANCE') { // Balance changed between request and execute } } ``` ## Handling Rate Limits When rate limited, the SDK throws with `retryAfter` information: ```typescript async function payWithRetry(params: PaymentRequestInput) { const maxRetries = 3; for (let i = 0; i < maxRetries; i++) { try { return await conto.payments.pay(params); } catch (error) { if (error.code === 'RATE_LIMITED' && i < maxRetries - 1) { const waitTime = error.retryAfter || 5; console.log(`Rate limited. Waiting ${waitTime}s...`); await new Promise(r => setTimeout(r, waitTime * 1000)); continue; } throw error; } } } ``` ## Handling Timeouts For long-running requests, handle timeouts: ```typescript const conto = new Conto({ apiKey: process.env.CONTO_API_KEY!, timeout: 60000 // 60 seconds }); try { await conto.payments.pay({ ... }); } catch (error) { if (error.code === 'TIMEOUT') { console.log('Request timed out'); // Don't automatically retry payments - check status first! const status = await conto.payments.status(requestId); if (status.transaction) { console.log('Payment was actually submitted:', status.transaction.txHash); } } } ``` ## Retry Strategy ### Built-in Automatic Retry The SDK automatically retries transient failures with exponential backoff. You don't need to implement retry logic yourself for most cases. **Built-in behavior:** - Retries up to **3 times** on `429` (rate limited) and `5xx` (server errors) - Respects `Retry-After` headers from the server - Exponential backoff: 1s → 2s → 4s (capped at 10s) - **Does not retry** client errors (`4xx` except `429`) or auth failures ```typescript // The SDK handles retries automatically — just call the method const result = await conto.payments.pay({ amount: 100, recipientAddress: '0x...' }); // If the server returns 429 or 503, the SDK waits and retries automatically ``` ### Custom Retry for Application Logic For application-level retry logic (e.g., re-requesting after a denial), use a custom wrapper: ```typescript async function withRetry ( fn: () => Promise , maxRetries = 3, baseDelay = 1000 ): Promise { let lastError: Error; for (let i = 0; i < maxRetries; i++) { try { return await fn(); } catch (error) { lastError = error; // Don't retry certain errors if (error instanceof Error && 'code' in error) { const sdkError = error as ContoError & { retryAfter?: number }; if (['AUTH_FAILED', 'PAYMENT_DENIED', 'VALIDATION_ERROR'].includes(sdkError.code)) { throw error; // Non-retryable } if (sdkError.code === 'RATE_LIMITED') { const delay = (sdkError.retryAfter ?? 0) * 1000 || baseDelay * Math.pow(2, i); await new Promise(r => setTimeout(r, delay)); continue; } } // Exponential backoff for other errors const delay = baseDelay * Math.pow(2, i); await new Promise(r => setTimeout(r, delay)); } } throw lastError!; } // Usage for application-level retries const result = await withRetry(() => conto.payments.pay({ amount: 100, recipientAddress: '0x...' })); ``` ## Logging Errors ```typescript async function loggedPayment(params: PaymentRequestInput) { try { const result = await conto.payments.pay(params); console.log('Payment successful', { txHash: result.txHash, amount: result.amount }); return result; } catch (error) { console.error('Payment failed', { code: error.code, message: error.message, params: { amount: params.amount, recipient: params.recipientAddress, purpose: params.purpose } }); throw error; } } ``` ## Best Practices Never let payment errors crash your application: ```typescript // Bad await conto.payments.pay({ ... }); // Good try { await conto.payments.pay({ ... }); } catch (error) { // Handle gracefully } ``` Don't just catch generic errors: ```typescript // Bad catch (error) { console.log('Something went wrong'); } // Good catch (error) { if (error.code === 'INSUFFICIENT_BALANCE') { // Specific handling } } ``` Be careful with retries on payment execution: ```typescript // Dangerous - might double-pay await withRetry(() => conto.payments.execute(requestId)); // Safe - check status first const status = await conto.payments.status(requestId); if (!status.transaction) { await conto.payments.execute(requestId); } ``` Always log error details for debugging: ```typescript catch (error) { console.error('Payment error', { code: error.code, status: error.status, message: error.message }); } ``` ## Next Steps ### Examples Link: https://conto.finance/docs/sdk/examples See complete integration examples ### API Reference Link: https://conto.finance/api-docs View the REST API (Swagger UI) --- # SDK Examples Complete examples for common integration patterns. ## Basic Payment ```typescript import { Conto } from '@conto/sdk'; const conto = new Conto({ apiKey: process.env.CONTO_API_KEY! }); // Simple one-step payment const result = await conto.payments.pay({ amount: 50.00, recipientAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f...', recipientName: 'OpenAI', purpose: 'GPT-4 API credits', category: 'AI_SERVICES' }); console.log('TX Hash:', result.txHash); ``` ## Two-Step Payment with Approval Handling ```typescript import { Conto, ContoError } from '@conto/sdk'; async function makePayment(params: { amount: number; recipient: string; purpose: string; }) { const conto = new Conto({ apiKey: process.env.CONTO_API_KEY! }); // Step 1: Request authorization const request = await conto.payments.request({ amount: params.amount, recipientAddress: params.recipient, purpose: params.purpose }); // Step 2: Handle different statuses switch (request.status) { case 'APPROVED': const result = await conto.payments.execute(request.requestId); return { success: true, txHash: result.txHash }; case 'DENIED': return { success: false, error: 'Payment denied', reasons: request.reasons, violations: request.violations }; case 'REQUIRES_APPROVAL': return { success: false, pending: true, requestId: request.requestId, message: 'Awaiting manual approval' }; } } ``` ## LangChain Tool Integration ```typescript import { DynamicTool } from 'langchain/tools'; import { Conto, ContoError } from '@conto/sdk'; const conto = new Conto({ apiKey: process.env.CONTO_API_KEY! }); export const paymentTool = new DynamicTool({ name: 'make_payment', description: `Make a stablecoin payment to a recipient. Input: JSON with amount, recipientAddress, recipientName, purpose. Returns: Transaction details or error message.`, func: async (input: string) => { try { const params = JSON.parse(input); const result = await conto.payments.pay({ amount: params.amount, recipientAddress: params.recipientAddress, recipientName: params.recipientName, purpose: params.purpose, category: params.category || 'OPERATIONS' }); return JSON.stringify({ success: true, transactionHash: result.txHash, amount: result.amount, explorerUrl: result.explorerUrl }); } catch (error) { if (error instanceof ContoError) { return JSON.stringify({ success: false, error: error.message, code: error.code }); } return JSON.stringify({ success: false, error: 'Unexpected error' }); } } }); ``` ## OpenAI Function Calling ```typescript import OpenAI from 'openai'; import { Conto } from '@conto/sdk'; const openai = new OpenAI(); const conto = new Conto({ apiKey: process.env.CONTO_API_KEY! }); // Define the function const paymentFunction = { name: 'make_payment', description: 'Make a stablecoin payment to a vendor or service', parameters: { type: 'object', properties: { amount: { type: 'number', description: 'Amount in USDC' }, recipientAddress: { type: 'string', description: 'Ethereum address' }, recipientName: { type: 'string', description: 'Recipient name' }, purpose: { type: 'string', description: 'Payment purpose' } }, required: ['amount', 'recipientAddress', 'purpose'] } }; // Handle function calls async function handleFunctionCall(name: string, args: any) { if (name === 'make_payment') { const result = await conto.payments.pay({ amount: args.amount, recipientAddress: args.recipientAddress, recipientName: args.recipientName, purpose: args.purpose }); return { success: true, txHash: result.txHash }; } } ``` ## Express API Endpoint ```typescript import express from 'express'; import { Conto, ContoError } from '@conto/sdk'; const app = express(); const conto = new Conto({ apiKey: process.env.CONTO_API_KEY! }); app.post('/api/payments', async (req, res) => { try { const { amount, recipientAddress, purpose } = req.body; // Validate input if (!amount || !recipientAddress) { return res.status(400).json({ error: 'Missing required fields' }); } // Make payment const result = await conto.payments.pay({ amount, recipientAddress, purpose }); res.json({ success: true, transactionId: result.transactionId, txHash: result.txHash, explorerUrl: result.explorerUrl }); } catch (error) { if (error instanceof ContoError) { res.status(error.status).json({ success: false, error: error.message, code: error.code }); } else { res.status(500).json({ success: false, error: 'Internal server error' }); } } }); ``` ## Python SDK Wrapper ```python import requests from typing import Optional, Dict, Any class ContoSDK: def __init__(self, api_key: str, base_url: str = "https://conto.finance"): self.api_key = api_key self.base_url = base_url self.headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } def request_payment( self, amount: float, recipient_address: str, recipient_name: Optional[str] = None, purpose: Optional[str] = None, category: Optional[str] = None ) -> Dict[str, Any]: payload = { "amount": amount, "recipientAddress": recipient_address } if recipient_name: payload["recipientName"] = recipient_name if purpose: payload["purpose"] = purpose if category: payload["category"] = category response = requests.post( f"{self.base_url}/api/sdk/payments/request", headers=self.headers, json=payload ) response.raise_for_status() return response.json() def execute_payment(self, request_id: str) -> Dict[str, Any]: response = requests.post( f"{self.base_url}/api/sdk/payments/{request_id}/execute", headers=self.headers ) response.raise_for_status() return response.json() def pay(self, amount: float, recipient_address: str, **kwargs) -> Dict[str, Any]: request = self.request_payment(amount, recipient_address, **kwargs) if request["status"] != "APPROVED": raise Exception(f"Payment denied: {request['reasons']}") return self.execute_payment(request["requestId"]) # Usage conto = ContoSDK("conto_agent_xxx") result = conto.pay( amount=50.00, recipient_address="0x1234...", recipient_name="OpenAI", purpose="API credits" ) print(f"Transaction hash: {result['txHash']}") ``` ## Single-Call Payment with autoExecute Skip the two-step flow entirely by using `autoExecute: true` — request authorization and execute the payment in one API call. ```typescript const response = await fetch('https://conto.finance/api/sdk/payments/request', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.CONTO_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ amount: 50.00, recipientAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f...', recipientName: 'OpenAI', purpose: 'GPT-4 API credits', category: 'AI_SERVICES', autoExecute: true }) }); const result = await response.json(); switch (result.status) { case 'EXECUTED': console.log('Payment complete in one call!'); console.log('TX Hash:', result.execution.txHash); console.log('Explorer:', result.execution.explorerUrl); console.log('Currency:', result.currency); console.log('Chain:', result.chain.chainName); break; case 'APPROVED': if (result.autoExecuteError) { // Auto-execute failed, fall back to manual execute console.log('Auto-execute failed:', result.autoExecuteError); const execResult = await fetch(result.executeUrl, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.CONTO_API_KEY}` } }).then(r => r.json()); console.log('Manual execute TX:', execResult.txHash); } else { // autoExecute was silently ignored (missing payments:execute scope) // Execute manually const execResult = await fetch(result.executeUrl, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.CONTO_API_KEY}` } }).then(r => r.json()); } break; case 'DENIED': console.log('Payment denied:', result.reasons); if (result.context?.nextSteps) { console.log('Suggested actions:', result.context.nextSteps); } break; case 'REQUIRES_APPROVAL': console.log('Awaiting manual approval'); break; } ``` ## Agent Bootstrap with Setup Endpoint Call `GET /api/sdk/setup` on startup to understand your agent's full configuration — wallets, policies, counterparties, and capabilities. ```typescript async function bootstrapAgent() { const config = await fetch('https://conto.finance/api/sdk/setup', { headers: { 'Authorization': `Bearer ${process.env.CONTO_API_KEY}` } }).then(r => r.json()); console.log('Agent:', config.agent.name, `(${config.agent.status})`); console.log('Scopes:', config.scopes.join(', ')); // Check capabilities if (config.capabilities.canAutoExecute) { console.log('Can auto-execute payments (single-call)'); } console.log('Max single payment:', config.capabilities.maxSinglePayment); console.log('Remaining daily budget:', config.capabilities.remainingDailyBudget); // List wallets for (const wallet of config.wallets) { console.log(`Wallet ${wallet.address} (${wallet.custodyType})`); console.log(` Chain: ${wallet.chainName} | Balance: ${wallet.balance} ${wallet.currency}`); console.log(` Executable: ${wallet.isExecutable}`); console.log(` Limits: $${wallet.limits.perTransaction}/tx, $${wallet.limits.daily}/day`); } // List known counterparties for (const cp of config.counterparties) { console.log(`Counterparty: ${cp.name} (${cp.trustLevel}) - ${cp.transactionCount} txns`); } return config; } ``` ## Programmatic Counterparty Creation Agents can create counterparties before making payments to new recipients. This avoids "unknown counterparty" policy denials. ```typescript async function ensureCounterparty(address: string, name: string, category?: string) { const response = await fetch('https://conto.finance/api/sdk/counterparties', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.CONTO_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ name, address, type: 'VENDOR', category: category || 'OPERATIONS' }) }); const counterparty = await response.json(); console.log(`Counterparty ${counterparty.created ? 'created' : 'updated'}: ${counterparty.name}`); console.log(`Trust: ${counterparty.trustLevel} (${counterparty.trustScore})`); return counterparty; } // Usage: create counterparty then pay await ensureCounterparty('0x1234...', 'Anthropic', 'AI_SERVICES'); const result = await conto.payments.pay({ amount: 100, recipientAddress: '0x1234...', purpose: 'Claude API credits' }); ``` ## Requesting Policy Exceptions When a payment is denied by policy, agents can request an exception for human review instead of failing silently. ```typescript async function requestException(type: string, reason: string, details?: object) { const response = await fetch('https://conto.finance/api/sdk/policies/exceptions', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.CONTO_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ type, // ADD_TO_WHITELIST, INCREASE_SPEND_LIMIT, etc. reason, urgency: 'NORMAL', details }) }); const exception = await response.json(); console.log(`Exception ${exception.exceptionId} submitted (${exception.status})`); return exception; } // Example: payment denied, request whitelist addition const request = await conto.payments.request({ amount: 100, recipientAddress: '0xNewVendor...', purpose: 'New service subscription' }); if (request.status === 'DENIED') { const hasWhitelistViolation = request.violations?.some( v => v.type === 'WHITELIST_VIOLATION' ); if (hasWhitelistViolation) { await requestException('ADD_TO_WHITELIST', 'Need to pay new vendor for service subscription', { counterpartyAddress: '0xNewVendor...', counterpartyName: 'New Service Inc.', amount: 100 }); } } ``` ## Batch Payments ```typescript import { Conto } from '@conto/sdk'; interface PaymentItem { recipient: string; amount: number; purpose: string; } async function batchPayments(items: PaymentItem[]) { const conto = new Conto({ apiKey: process.env.CONTO_API_KEY! }); const results = []; for (const item of items) { try { const result = await conto.payments.pay({ amount: item.amount, recipientAddress: item.recipient, purpose: item.purpose }); results.push({ recipient: item.recipient, success: true, txHash: result.txHash }); } catch (error) { results.push({ recipient: item.recipient, success: false, error: error.message }); } // Rate limit: wait between requests await new Promise(r => setTimeout(r, 500)); } return results; } ``` ## Webhook Handler (Approval Notifications) ```typescript import express from 'express'; import { Conto } from '@conto/sdk'; const app = express(); const conto = new Conto({ apiKey: process.env.CONTO_API_KEY! }); // Store pending payments const pendingPayments = new Map void; }>(); // Request payment and wait for approval async function requestPaymentWithApproval(params: { amount: number; recipient: string; purpose: string; }) { const request = await conto.payments.request({ amount: params.amount, recipientAddress: params.recipient, purpose: params.purpose }); if (request.status === 'APPROVED') { return conto.payments.execute(request.requestId); } if (request.status === 'REQUIRES_APPROVAL') { // Wait for webhook callback return new Promise((resolve) => { pendingPayments.set(request.requestId, { amount: params.amount, recipient: params.recipient, resolve }); }); } throw new Error(`Payment denied: ${request.reasons.join(', ')}`); } // Webhook endpoint (called when payment is approved) app.post('/webhooks/conto', async (req, res) => { const { event, requestId } = req.body; if (event === 'payment.approved') { const pending = pendingPayments.get(requestId); if (pending) { const result = await conto.payments.execute(requestId); pending.resolve(result); pendingPayments.delete(requestId); } } res.json({ received: true }); }); ``` ## MCP Server Configuration For Claude Desktop integration: ```json { "mcpServers": { "conto": { "command": "npx", "args": ["@conto_finance/mcp-server"], "env": { "CONTO_API_KEY": "conto_agent_xxx..." } } } } ``` Then Claude can make payments naturally: > "Pay $50 to 0x123... for API credits" ## Next Steps ### Policies Link: https://conto.finance/docs/policies/overview Configure spending policies ### API Reference Link: https://conto.finance/api-docs View the REST API (Swagger UI) --- # Nous Hermes Skill The Conto skill for [Nous Hermes](https://hermes-agent.nousresearch.com/) adds fine-grained spending policies to any AI agent. It checks every payment against 40+ policy rule types before money leaves the wallet. ## How It Works ``` Agent wants to pay 50 pathUSD to 0xabc... | v Skill calls POST /api/sdk/payments/approve | v Conto evaluates all policy rules | +---> APPROVED: agent proceeds with payment +---> DENIED: agent stops, reports violations +---> REQUIRES_APPROVAL: agent pauses for human sign-off ``` Conto supports two wallet modes: - **Integrated (PRIVY/SPONGE)** -- Your wallet provider holds the keys (Privy or Sponge, respectively). Conto evaluates policies and orchestrates execution through the provider. One API call handles policy check + execution. - **External** -- Agent holds the keys. Agent calls approve, transfers itself, then confirms. Both modes evaluate the same 40+ policy rule types. ## Install ### Requirements `conto-check.sh` relies on `curl`, `jq`, and `python3`. `curl` and `python3` ship with macOS and most Linux distros; install `jq` via your package manager if missing (`brew install jq`, `apt install jq`). `python3` is used by the `setup` subcommand to run a short-lived localhost callback server during browser auth. ### From well-known endpoint (recommended) ```bash hermes skills install well-known:https://conto.finance/.well-known/skills/conto ``` This fetches `SKILL.md` and `conto-check.sh` from the well-known discovery endpoint and installs them into `~/.hermes/skills/conto/`. ### Manual install Copy the skill files directly: ```bash cp -r skills/conto-hermes ~/.hermes/skills/conto ``` Or add the directory to your Hermes config (`~/.hermes/config.yaml`): ```yaml skills: external_dirs: - /path/to/skills/conto-hermes ``` ## Configure ### Quick Setup (Recommended) After installing, run the setup command with your agent name and wallet address: ```bash conto-check.sh setup "my-hermes-agent" "0xYourWalletAddress" EVM 42431 ``` This opens your browser for Conto login. After you approve, the agent is automatically provisioned with: - An agent record linked to your organization - Your wallet registered as EXTERNAL custody - Default spend limits ($100/tx, $500/day) - An SDK key written to `~/.hermes/.env` `EXTERNAL` custody keeps signing in Hermes or your existing wallet stack. Conto can pre-approve and record the payment flow, but it cannot cryptographically block a direct transfer signed outside Conto. To find your wallet address, ask your agent: `What is my wallet address?` Verify it works: ```bash conto-check.sh budget ``` ### Manual Setup (Alternative) If the browser setup doesn't work: #### 1. Connect your agent in Conto Sign in to the [Conto dashboard](https://conto.finance) and connect your agent. You'll register your wallet in the walkthrough below. #### 2. Generate an SDK key Go to **Agents > your agent > SDK Keys > Generate New Key**. - Select **Standard** for payment approval only - Select **Admin** if you want the agent to create/manage policies #### 3. Add to Hermes env Add your SDK key to `~/.hermes/.env`: ```bash CONTO_SDK_KEY=conto_agent_your_key_here CONTO_API_URL=https://conto.finance ``` ## Usage ### Telegram / Discord / WhatsApp ``` /conto list my policies /conto create a $200 per-transaction limit Send 50 pathUSD to 0x742d... on Tempo ``` ### CLI ```bash hermes agent -m "/conto list my policies" hermes agent -m "Send 50 pathUSD to 0x742d... on Tempo" ``` ## Standard vs Admin SDK Keys | Capability | Standard | Admin | | ------------------------------ | -------- | ----- | | Check payment policies | Yes | Yes | | Confirm payments | Yes | Yes | | Pre-authorize x402 calls | Yes | Yes | | Read policies and transactions | Yes | Yes | | Create/update/delete policies | No | Yes | | Manage agents and wallets | No | Yes | ## Create Policies from Hermes With an admin SDK key, manage policies via natural language: ``` /conto create a policy that limits each transaction to 200 pathUSD /conto create a policy that only allows API_PROVIDER and CLOUD categories /conto block address 0xbad... from receiving payments /conto create a policy that requires approval for payments over 500 pathUSD /conto list my policies /conto delete the blocklist policy ``` ## Test Policies After creating policies, verify enforcement: ``` # Should approve (within limits) /conto check if a 10 pathUSD payment to 0x742d... is allowed # Should deny (over per-tx limit) /conto check if a 500 pathUSD payment to 0x742d... is allowed # Should deny (blocked address) /conto check if a 1 pathUSD payment to 0xbad... is allowed # Should deny (wrong category) /conto check if a 10 pathUSD payment to 0x742d... for gambling is allowed ``` ## Supported Policy Types | Type | What it controls | | --------------------------------------------------- | -------------------------------- | | `MAX_AMOUNT` | Per-transaction cap | | `DAILY_LIMIT` / `WEEKLY_LIMIT` / `MONTHLY_LIMIT` | Cumulative spend caps | | `ALLOWED_CATEGORIES` / `BLOCKED_CATEGORIES` | Category whitelist/blocklist | | `ALLOWED_COUNTERPARTIES` / `BLOCKED_COUNTERPARTIES` | Address whitelist/blocklist | | `TIME_WINDOW` / `DAY_OF_WEEK` | Business hours, allowed days | | `BLACKOUT_PERIOD` | Maintenance windows | | `VELOCITY_LIMIT` | Transaction rate limiting | | `REQUIRE_APPROVAL_ABOVE` | Human approval threshold | | `GEOGRAPHIC_RESTRICTION` | Country/OFAC restrictions | | `CONTRACT_ALLOWLIST` | DeFi contract restrictions | | `X402_PRICE_CEILING` | Max per x402 API call | | `X402_ALLOWED_SERVICES` / `X402_BLOCKED_SERVICES` | x402 service allowlist/blocklist | | `X402_MAX_PER_SERVICE` | Per-service daily cap | ## End-to-End Example: Pay a Vendor on Tempo Testnet This walkthrough sends a real payment through the full external-wallet flow: **approve → transfer onchain → confirm back to Conto**. ### Prerequisites - Agent connected in Conto - SDK key (`conto_agent_...`) configured in `~/.hermes/.env` - At least one policy (e.g. a 200 pathUSD per-transaction limit) - pathUSD in the agent's wallet ([faucet](https://faucet.tempo.network)) ### Step 1 — Create a spending policy ``` /conto create a policy that limits each transaction to 200 pathUSD ``` The skill calls `POST /api/sdk/policies` and returns the policy ID. Verify it: ``` /conto list my policies ``` ### Step 2 — Request a payment (approve) ``` Send 50 pathUSD to 0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18 on Tempo ``` Behind the scenes the skill calls: ```bash curl -X POST https://conto.finance/api/sdk/payments/approve \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 50, "recipientAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18", "senderAddress": "0x1a2b3c4d5e6f...", "chainId": 42431, "purpose": "Vendor payment" }' ``` Conto evaluates every assigned policy. If approved, the response includes an `approvalId` and `approvalToken`. ### Step 3 — Transfer onchain Because this is an external wallet, the agent transfers pathUSD itself using its own keys. The skill handles this automatically — you don't need to do anything extra. ### Step 4 — Confirm back to Conto After the onchain transfer succeeds, the skill reports the transaction hash: ```bash curl -X POST https://conto.finance/api/sdk/payments/APPROVAL_ID/confirm \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "txHash": "0xabc123...", "approvalToken": "a1b2c3d4..." }' ``` The `approvalToken` is the token from the Step 2 approval response. Conto records the payment, updates spend counters, and the transaction appears in the dashboard. ### Step 5 — Verify the result ``` /conto show my recent transactions ``` Or check the [Conto dashboard](https://conto.finance) under **Transactions** — you'll see the confirmed payment with its explorer link. ### What happens if a policy blocks the payment? Try exceeding the limit: ``` Send 300 pathUSD to 0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18 on Tempo ``` The skill returns a denial with the specific violation (e.g. `MAX_AMOUNT: 300 exceeds limit of 200`). No onchain transfer occurs. The denied attempt appears under **Alerts** in the dashboard. --- ## Verify in Dashboard After running payments through the skill, check the [Conto dashboard](https://conto.finance): - **Transactions** -- confirmed payments with tx hashes and explorer links - **Alerts** -- denied payment attempts with violation details - **Agents** -- spend tracking (daily/weekly/monthly used) ## Run the E2E Test For automated testing against a local Conto instance: ```bash # Terminal 1 npm run dev # Terminal 2 npx tsx scripts/test-openclaw-skill.ts ``` This creates an isolated test agent with 4 policies, runs 10 tests verifying each policy type, and cleans up afterward. ## Rate Limits | Endpoint type | Limit | | ------------------------------------------------------------------ | -------------------------- | | Payment endpoints (`/approve`, `/request`, `/execute`, `/confirm`) | 60 requests/min per agent | | Read endpoints (`/wallets`, `/policies`, `/transactions`, etc.) | 120 requests/min per agent | When rate limited, the API returns `429` with a `Retry-After` header. The skill handles retries automatically. ## Troubleshooting Verify `CONTO_API_URL` in `~/.hermes/.env` is correct. For the hosted platform, use `https://conto.finance`. For local development, use `http://localhost:3006`. Test connectivity: ```bash curl https://conto.finance/api/sdk/setup \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` If you get a valid JSON response, the URL is reachable. SDK keys are scoped to a single agent. Check that: - The key starts with `conto_agent_` (not `conto_org_`) - The key hasn't been revoked in the dashboard under **Agents > SDK Keys** - You're using the correct key for the correct agent Generate a new key if needed: **Agents > your agent > SDK Keys > Generate New Key**. The denial response includes a `violations` array listing every rule that failed. Common causes: - **Spend limit exceeded** — check daily/weekly/monthly counters in the dashboard under **Agents > Spend Tracking** - **Counterparty not on allowlist** — if you have an `ALLOWED_COUNTERPARTIES` policy, the recipient must be listed - **Outside time window** — `TIME_WINDOW` and `DAY_OF_WEEK` rules use the timezone set on the policy - **Category mismatch** — if `ALLOWED_CATEGORIES` is set and no `category` is provided in the request, the rule is skipped (not denied). But if `BLOCKED_CATEGORIES` matches, it denies. Use the dry-run check to debug without attempting a real payment: ``` /conto check if a 50 pathUSD payment to 0x742d... is allowed ``` In external wallet mode, Conto only enforces policy — the agent must transfer funds itself. If the approve call succeeds but no transfer happens: - Check the agent has enough pathUSD in its wallet - Check the Hermes agent logs for transfer errors - Ensure the wallet address registered in Conto matches the agent's actual wallet If the transfer succeeded but Conto doesn't show it, the confirm call may have failed. Retry: ```bash curl -X POST https://conto.finance/api/sdk/payments/APPROVAL_ID/confirm \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{"txHash": "0x...", "approvalToken": "a1b2c3d4..."}' ``` Policy management requires an **Admin** SDK key. Standard keys can only read policies and approve/confirm payments. Check the key type in the dashboard under **Agents > SDK Keys** — the scope column shows `standard` or `admin`. Policies must be **assigned to the agent**. Creating a policy alone doesn't activate it. Assign via the dashboard (**Policies > Assign to Agent**) or via the API: ```bash curl -X POST https://conto.finance/api/agents/AGENT_ID/policies \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{"policyId": "POLICY_ID"}' ``` Also verify the policy status is `ACTIVE` (not `DRAFT` or `DISABLED`). Check the skill directory exists: ```bash ls ~/.hermes/skills/conto/SKILL.md ``` If using `external_dirs`, verify the path in `~/.hermes/config.yaml` points to a directory containing `SKILL.md`. Hermes scans for `SKILL.md` files to discover skills. Restart the Hermes agent after installing or updating skills. --- # SDK Installation The Conto SDK provides a type-safe interface for AI agents to request and execute payments. ## Installation ```bash npm npm install @conto/sdk ``` ```bash yarn yarn add @conto/sdk ``` ```bash pnpm pnpm add @conto/sdk ``` ```bash bun bun add @conto/sdk ``` ## Packages | Package | Scope | Description | |---------|-------|-------------| | `@conto/sdk` | `@conto` | TypeScript SDK for payment operations | | `@conto_finance/create-conto-agent` | `@conto_finance` | CLI quickstart tool (`npx @conto_finance/create-conto-agent`) | | `@conto_finance/mcp-server` | `@conto_finance` | MCP server for Claude Desktop | ## Requirements - Node.js 18+ or Bun - TypeScript 4.7+ (optional but recommended) ## Basic Setup ```typescript import { Conto } from '@conto/sdk'; const conto = new Conto({ apiKey: process.env.CONTO_API_KEY, // conto_agent_xxx... }); ``` ## Configuration Options | Option | Type | Default | Description | |--------|------|---------|-------------| | `apiKey` | string | Required | Your agent's SDK API key | | `baseUrl` | string | `https://conto.finance` | API base URL | | `timeout` | number | `30000` | Request timeout in milliseconds | Info: The SDK automatically retries transient failures (429 rate limits, 5xx server errors) with exponential backoff — up to 3 retries with 1s/2s/4s delays. It also respects `Retry-After` headers from the server. ### Full Configuration Example ```typescript const conto = new Conto({ apiKey: process.env.CONTO_API_KEY, timeout: 30000 // 30 seconds }); ``` ## Environment Variables We recommend using environment variables for configuration: ```bash .env CONTO_API_KEY=conto_agent_abc123def456... ``` ```typescript const conto = new Conto({ apiKey: process.env.CONTO_API_KEY!, }); ``` ## TypeScript Support The SDK is written in TypeScript and includes full type definitions: ```typescript import { Conto, ContoConfig, PaymentRequestInput, PaymentRequestResult, PaymentExecuteResult, ContoError } from '@conto/sdk'; // All types are automatically inferred const request: PaymentRequestResult = await conto.payments.request({ amount: 100, recipientAddress: '0x...', }); ``` ## Framework Integration ### Next.js ```typescript // lib/conto.ts import { Conto } from '@conto/sdk'; export const conto = new Conto({ apiKey: process.env.CONTO_API_KEY!, }); ``` ### Express ```typescript // app.ts import express from 'express'; import { Conto } from '@conto/sdk'; const app = express(); const conto = new Conto({ apiKey: process.env.CONTO_API_KEY!, }); app.post('/pay', async (req, res) => { const result = await conto.payments.pay(req.body); res.json(result); }); ``` ### Serverless (AWS Lambda) ```typescript import { Conto } from '@conto/sdk'; // Initialize outside handler for connection reuse const conto = new Conto({ apiKey: process.env.CONTO_API_KEY!, timeout: 10000 // Lower timeout for Lambda }); export const handler = async (event: any) => { const result = await conto.payments.pay(JSON.parse(event.body)); return { statusCode: 200, body: JSON.stringify(result) }; }; ``` ## Verifying Installation Test your installation: ```typescript import { Conto } from '@conto/sdk'; async function verifySetup() { const conto = new Conto({ apiKey: process.env.CONTO_API_KEY!, }); // Try a minimal request (will fail with small amount but confirms connectivity) const request = await conto.payments.request({ amount: 0.01, recipientAddress: '0x0000000000000000000000000000000000000000', purpose: 'SDK verification test' }); console.log('SDK connected successfully!'); console.log('Request status:', request.status); } verifySetup().catch(console.error); ``` ## Next Steps ### Authentication Link: https://conto.finance/docs/sdk/authentication Learn about SDK authentication ### Payments Link: https://conto.finance/docs/sdk/payments Make your first payment --- # Machine Spend Machine Spend gives you a unified read layer over paid service usage recorded through Conto's x402 and MPP flows. Use it to answer questions like: - Which paid services is this agent using most? - Which endpoints are driving the most spend? - Is spend concentrated in x402, MPP, or both? - Are there active x402 or MPP anomaly alerts for this agent? Info: Machine Spend is additive to existing x402 and MPP tracking. It does not replace protocol-specific endpoints like `/api/sdk/x402/services` or `/api/sdk/mpp/budget`; it gives you one combined view on top of them. ## What It Includes The machine-spend endpoints aggregate existing `A2S` transactions and protocol alerts into a shared view: - total spend and call volume - service-level breakdowns - endpoint-level breakdowns - x402 vs MPP split - micropayment counts - recent x402 / MPP anomaly alerts This is especially useful when one agent uses multiple paid APIs across both protocols. ## Authentication These endpoints use the normal **agent SDK key** flow: ```bash Authorization: Bearer conto_agent_xxx ``` Required scope: - `analytics:read` ## Get a Summary Returns a top-level summary plus recent machine-spend alerts for the authenticated agent. ```bash curl "https://conto.finance/api/sdk/service-spend/summary?period=30d&protocol=all" \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` ### Query Parameters | Parameter | Type | Default | Description | | ---------- | ------ | ------- | --------------------------------- | | `period` | string | `30d` | One of `24h`, `7d`, `30d`, `all` | | `protocol` | string | `all` | One of `all`, `x402`, `mpp` | | `limit` | number | `10` | Number of recent alerts to return | ### Example Response ```json { "filters": { "period": "30d", "protocol": "all", "agentId": "agt_123", "startDate": "2026-03-18T00:00:00.000Z" }, "summary": { "totalSpend": 14.73, "totalCalls": 219, "activeServices": 6, "activeAgents": 1, "activeAlerts": 1, "micropaymentTransactions": 188, "x402Spend": 4.12, "x402Calls": 173, "mppSpend": 10.61, "mppCalls": 46 }, "recentAlerts": [ { "id": "alt_123", "protocol": "x402", "alertType": "X402_PRICE_SPIKE", "severity": "MEDIUM", "status": "ACTIVE", "title": "x402 price spike: api.example.com", "message": "api.example.com is charging more than its historical average", "createdAt": "2026-04-16T19:02:11.000Z", "organizationName": "Acme", "agentName": "research-agent" } ] } ``` ## List Services And Endpoints Returns the top service domains and endpoints for the authenticated agent. ```bash curl "https://conto.finance/api/sdk/service-spend/services?period=7d&protocol=x402&limit=20" \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` ### Query Parameters | Parameter | Type | Default | Description | | ---------- | ------ | ------- | ---------------------------------------- | | `period` | string | `30d` | One of `24h`, `7d`, `30d`, `all` | | `protocol` | string | `all` | One of `all`, `x402`, `mpp` | | `limit` | number | `10` | Number of services / endpoints to return | ### Example Response ```json { "filters": { "period": "7d", "protocol": "x402", "agentId": "agt_123", "startDate": "2026-04-10T00:00:00.000Z" }, "services": [ { "protocol": "x402", "serviceDomain": "api.example.com", "totalSpend": 2.41, "totalCalls": 120, "avgPricePerCall": 0.0201, "minPricePerCall": 0.01, "maxPricePerCall": 0.05, "firstActivityAt": "2026-04-11T03:44:52.000Z", "lastActivityAt": "2026-04-16T21:08:17.000Z" } ], "endpoints": [ { "protocol": "x402", "resourceUrl": "https://api.example.com/search", "serviceDomain": "api.example.com", "totalSpend": 1.84, "totalCalls": 92, "avgPricePerCall": 0.02, "lastActivityAt": "2026-04-16T21:08:17.000Z" } ] } ``` ## When To Use Machine Spend vs Protocol-Specific Endpoints Use **Machine Spend** when you want a unified service view across both protocols. Use **protocol-specific endpoints** when you need details unique to one rail: - `/api/sdk/x402/services` for x402 service-specific stats - `/api/sdk/x402/budget` for x402 burn rate and policy limit views - `/api/sdk/mpp/services` for MPP service-specific stats - `/api/sdk/mpp/budget` for MPP session and budget views ## Dashboard View Conto also exposes this data in the dashboard, where you can: - filter by protocol and time window - compare top services and endpoints - inspect top agents and organizations - review recent x402 / MPP anomaly alerts ## Next Steps ### x402 Payments Link: https://conto.finance/docs/sdk/x402-payments Record and control pay-per-call API spend ### MPP Payments Link: https://conto.finance/docs/sdk/mpp-payments Track session-based micropayment usage --- # MPP (Machine Payment Protocol) Payments Conto supports the Machine Payment Protocol (MPP) for session-based micropayments on the Tempo blockchain. MPP enables agents to open payment sessions, make incremental charges, and settle when done. ## How It Works ``` Agent opens session → Makes requests (charges accrue) → Session closes → Settlement ``` 1. Agent calls an MPP-enabled service and receives a 402 challenge 2. Agent pre-authorizes the session deposit through Conto policies 3. Agent opens an MPP session with a deposit budget 4. Agent makes requests, each consuming part of the deposit 5. Session closes and unused deposit is returned ## Pre-Authorization Before opening an MPP session, validate against policies and budget limits: ```bash curl -X POST https://conto.finance/api/sdk/mpp/pre-authorize \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 10.00, "recipientAddress": "0xServiceAddress", "resourceUrl": "https://api.service.com/stream", "intent": "session", "depositAmount": 10.00 }' ``` Conto derives `serviceDomain` from `resourceUrl`, so you do not need to send it separately. **Response (Approved):** ```json { "authorized": true, "wallet": { "id": "wal_123", "address": "0xAgentWallet", "chainId": "4217", "availableBalance": 500.0 }, "reasons": ["Within MPP session budget", "Service domain allowed"] } ``` ## Recording Transactions After MPP charges are settled, record them in Conto: ```bash curl -X POST https://conto.finance/api/sdk/mpp/record \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 3.50, "recipientAddress": "0xServiceAddress", "txHash": "0xabc123...", "resourceUrl": "https://api.service.com/stream", "sessionId": "mpp_session_xyz", "scheme": "mpp", "walletId": "wal_123", "chainId": "4217" }' ``` If you settle multiple calls together, keep the top-level fields for the aggregate settlement and send per-call detail records in `batchItems`: ```bash curl -X POST https://conto.finance/api/sdk/mpp/record \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 3.50, "recipientAddress": "0xServiceAddress", "txHash": "0xsettlement123...", "resourceUrl": "https://api.service.com/stream", "sessionId": "mpp_session_xyz", "batchItems": [ { "amount": 1.00, "resourceUrl": "https://api.service.com/stream?chunk=1", "credentialId": "cred_1" }, { "amount": 2.50, "resourceUrl": "https://api.service.com/stream?chunk=2", "credentialId": "cred_2" } ] }' ``` ## Querying Services View MPP services your agent has used: ```bash GET /api/sdk/mpp/services ``` ## Budget Tracking Check remaining MPP budget: ```bash GET /api/sdk/mpp/budget ``` If an agent has an active approved budget request, `/pre-authorize` returns a `budgetRequest` snapshot and `/record` decrements the remaining budget automatically. ## Unified Machine Spend View If this agent also uses x402 or multiple paid services, use the shared machine-spend endpoints for a combined view: ```bash GET /api/sdk/service-spend/summary?protocol=mpp GET /api/sdk/service-spend/services?protocol=mpp ``` See [Machine Spend](https://conto.finance/docs/sdk/machine-spend) for the combined x402 + MPP analytics view. ## MPP Policy Rules Configure MPP-specific policies to control session-based payments. See [Advanced Policies](https://conto.finance/docs/policies/advanced) for the full rule type reference. Key rule types: - `MPP_MAX_PER_REQUEST` — Cap per individual charge - `MPP_PRICE_CEILING` — Hard price ceiling - `MPP_SESSION_BUDGET` — Maximum deposit per session - `MPP_MAX_CONCURRENT_SESSIONS` — Limit active sessions - `MPP_MAX_SESSION_DURATION` — Maximum session length - `MPP_ALLOWED_SERVICES` — Allowlist of service domains - `MPP_BLOCKED_SERVICES` — Blocklist of service domains ## Supported Chain MPP payments are currently supported on the **Tempo** blockchain: | Property | Value | | -------- | ------------------------- | | Network | Tempo Mainnet | | Chain ID | 4217 | | Currency | USDC.e | | Explorer | https://explore.tempo.xyz | For Tempo Testnet and demo flows, use chain `42431` with `pathUSD`. ## Next Steps ### x402 Payments Link: https://conto.finance/docs/sdk/x402-payments HTTP 402 micropayments for APIs ### Machine Spend Link: https://conto.finance/docs/sdk/machine-spend View unified x402 and MPP service spend ### Advanced Policies Link: https://conto.finance/docs/policies/advanced Configure MPP-specific policy rules --- # OpenClaw Skill The Conto skill for [OpenClaw](https://github.com/openclaw/openclaw) adds fine-grained spending policies to any AI agent. It checks every payment against 40+ policy rule types before money leaves the wallet. ## How It Works ``` Agent wants to pay 50 pathUSD to 0xabc... | v Skill calls POST /api/sdk/payments/approve | v Conto evaluates all policy rules | +---> APPROVED: agent proceeds with payment +---> DENIED: agent stops, reports violations +---> REQUIRES_APPROVAL: agent pauses for human sign-off ``` Conto supports two wallet modes: - **Integrated (PRIVY/SPONGE)** -- Your wallet provider holds the keys (Privy or Sponge, respectively). Conto evaluates policies and orchestrates execution through the provider. One API call handles policy check + execution. - **External** -- Agent holds the keys. Agent calls approve, transfers itself, then confirms. Both modes evaluate the same 40+ policy rule types. ### Which endpoint do I call? | | Mode A (Integrated) | Mode B (External) | | --------------------- | -------------------------------- | -------------------------------- | | **Custody type** | PRIVY or SPONGE | EXTERNAL | | **Who holds keys** | Wallet provider | Your agent | | **Endpoint** | `POST /api/sdk/payments/request` | `POST /api/sdk/payments/approve` | | **Calls per payment** | 1 (with `autoExecute: true`) | 3 (approve → transfer → confirm) | | **Approval expiry** | 5 minutes | 10 minutes | | **chainId** | Resolved from wallet | **Required** in request body | Most OpenClaw agents use **Mode B** — the agent controls its wallet via MCP tools and Conto acts as the policy gate. ## Install ### Requirements `conto-check.sh` relies on `curl`, `jq`, and `python3`. `curl` and `python3` ship with macOS and most Linux distros; install `jq` via your package manager if missing (`brew install jq`, `apt install jq`). `python3` is used by the `setup` subcommand to run a short-lived localhost callback server during browser auth. ### Install the skill Install from [ClawHub](https://clawhub.ai/kwattana/conto): ```bash npx clawhub install conto ``` Or add the skill directly from the manifest: [conto.finance/skill.md](https://conto.finance/skill.md) Do not run `npm install @conto/sdk`. The OpenClaw skill uses `conto-check.sh` (installed by ClawHub) to call the Conto REST API directly. The `@conto/sdk` npm package is a separate TypeScript SDK for programmatic integrations and is not needed for OpenClaw. ### Quick Setup (Recommended) After installing, run the setup command with your agent name and wallet address: ```bash conto-check.sh setup "my-agent" "0xYourWalletAddress" EVM 42431 ``` This opens your browser for Conto login. After you approve, the agent is automatically provisioned with: - An agent record linked to your organization - Your wallet registered as EXTERNAL custody - Default spend limits ($100/tx, $500/day) - An SDK key written to `~/.openclaw/openclaw.json` To find your wallet address, ask your agent: `What is my wallet address?` **Arguments:** | Argument | Default | Description | | ---------------- | ---------- | ------------------------------------------------------------------------ | | `agent_name` | (required) | Name for your agent | | `wallet_address` | (required) | Your wallet address (0x... for EVM, base58 for Solana) | | `chain_type` | `EVM` | `EVM` or `SOLANA` | | `chain_id` | `42431` | Chain ID. Common: `8453` (Base), `42431` (Tempo Testnet), `1` (Ethereum) | Verify it works: ```bash conto-check.sh budget ``` You can adjust spend limits, add policies, and manage your agent anytime in the [Conto dashboard](https://conto.finance). ### Manual Setup (Alternative) If the browser setup doesn't work, you can configure manually. This requires four steps: 1. **Connect your agent in Conto** — Sign in to the [Conto dashboard](https://conto.finance) and connect your agent. This is how Conto knows which OpenClaw agent to track policies, spend limits, and transaction history for. 2. **Link your wallet** — Tell Conto which wallet your agent will be sending payments from. You can use an existing wallet like the one connected to your OpenClaw, or create a new wallet with Privy or Sponge directly in Conto. See [Find Your Wallet Address](#find-your-wallet-address) below. 3. **Generate an SDK key** — This is the credential your OpenClaw agent uses to authenticate with Conto. You'll add it to your OpenClaw config file. 4. **Add the SDK key to OpenClaw config** — Save the key to `~/.openclaw/openclaw.json` so the skill can authenticate with Conto. The next sections walk through each of these steps. ## Find Your Wallet Address Conto needs to know which wallet your agent controls so it can track spend limits and verify onchain transfers. How you get your wallet address depends on your setup. ### Option A: You already have a wallet (Sponge, AgentCash, etc.) If your OpenClaw agent already has a wallet through an MCP server like Sponge, you can retrieve the address directly. **Sponge MCP** — Ask your agent or run the `get_balance` tool. The response includes your wallet addresses for each chain: ``` > What are my wallet addresses? ``` The agent will return something like: ``` Tempo: 0x1a2b3c4d5e6f... Base: 0x1a2b3c4d5e6f... Solana: ABC123... ``` Copy the address for the chain you want to use (Tempo for testnet, Base or Solana for production). **AgentCash** — Use the `list_accounts` tool to see your wallet addresses across all supported networks. ### Option B: Create a new wallet in Conto (Privy or Sponge) If you don't have an existing wallet, you can create one directly in the Conto dashboard: 1. Go to the [Conto dashboard](https://conto.finance) 2. Navigate to **Wallets > Create Wallet** 3. Set custody type to **PRIVY** or **SPONGE** 4. Select the chain (e.g. Tempo Testnet for testing) 5. Click **Provision** — Conto creates the wallet onchain and shows you the address With a Privy wallet, Privy holds the keys and Conto orchestrates execution through Privy after policy approval — no separate transfer step needed. With a Sponge wallet, Sponge holds the keys and Conto orchestrates through Sponge in the same way. ### Option C: Use your own external wallet If you have a wallet you manage yourself (hardware wallet, MetaMask, etc.), you can register its address in Conto as an EXTERNAL wallet. With external wallets, your agent handles the onchain transfer itself after Conto approves the payment. EXTERNAL custody keeps full key control in your wallet stack. Conto can approve, deny, record, and alert on payments routed through Conto, but it cannot cryptographically block a direct transfer signed outside Conto. **Which option should I pick?** | Setup | Who holds keys | Payment flow | | ----------------------------- | ------------------------------- | -------------------------------------------------------------- | | **Existing OpenClaw wallet** | Your MCP provider (e.g. Sponge) | Agent calls approve → agent transfers via MCP → agent confirms | | **Privy (created in Conto)** | Privy | Agent calls approve → Conto orchestrates execution via Privy | | **Sponge (created in Conto)** | Sponge | Agent calls approve → Conto orchestrates execution via Sponge | | **External** | You | Agent calls approve → agent transfers itself → agent confirms | For most OpenClaw users with an existing wallet connected to their agent, register it as an **EXTERNAL** wallet in Conto. Your agent already has transfer tools through its MCP server — Conto just adds the policy layer on top. ## Configure ### 1. Connect your agent in Conto Sign in to the [Conto dashboard](https://conto.finance) and connect your agent. Give it a name and description that matches your OpenClaw agent. ### 2. Link your wallet Go to **Agents > your agent > Wallets > Link Wallet**. Enter your wallet address (see [Find Your Wallet Address](#find-your-wallet-address) above) and select the chain and custody type. Set initial spending limits for per-transaction, daily, and weekly caps. ### 3. Generate an SDK key Go to **Agents > your agent > SDK Keys > Generate New Key**. - Select **Standard** for payment approval only - Select **Admin** if you want the agent to create/manage policies ### 4. Add to OpenClaw config If you used `conto-check.sh setup` in [Quick Setup](#quick-setup-recommended), this file was created automatically and you can skip this step. Edit `~/.openclaw/openclaw.json`. If the file does not exist, create it with exactly this content (replace the key value with your actual SDK key): ```json { "skills": { "entries": { "conto": { "env": { "CONTO_SDK_KEY": "conto_agent_your_key_here", "CONTO_API_URL": "https://conto.finance" } } } } } ``` This file must be valid JSON. A missing comma, extra trailing comma, or mismatched brace will cause OpenClaw to fail on every command. If you see parse errors after editing this file, validate it with `cat ~/.openclaw/openclaw.json | jq .` or paste it into [jsonlint.com](https://jsonlint.com). ## Usage ### Telegram / Discord / WhatsApp ``` /conto list my policies /conto create a $200 per-transaction limit Send 50 pathUSD to 0x742d... on Tempo ``` ### CLI ```bash openclaw agent --agent main -m "/conto list my policies" openclaw agent --agent main -m "Send 50 pathUSD to 0x742d... on Tempo" ``` ## Standard vs Admin SDK Keys | Capability | Standard | Admin | | ------------------------------ | -------- | ----- | | Check payment policies | Yes | Yes | | Confirm payments | Yes | Yes | | Pre-authorize x402 calls | Yes | Yes | | Read policies and transactions | Yes | Yes | | Create/update/delete policies | No | Yes | | Manage agents and wallets | No | Yes | ## Create Policies from OpenClaw With an admin SDK key, manage policies via natural language: ``` /conto create a policy that limits each transaction to 200 pathUSD /conto create a policy that only allows API_PROVIDER and CLOUD categories /conto block address 0xbad... from receiving payments /conto create a policy that requires approval for payments over 500 pathUSD /conto list my policies /conto delete the blocklist policy ``` ## Test Policies After creating policies, verify enforcement: ``` # Should approve (within limits) /conto check if a 10 pathUSD payment to 0x742d... is allowed # Should deny (over per-tx limit) /conto check if a 500 pathUSD payment to 0x742d... is allowed # Should deny (blocked address) /conto check if a 1 pathUSD payment to 0xbad... is allowed # Should deny (wrong category) /conto check if a 10 pathUSD payment to 0x742d... for gambling is allowed ``` ## Supported Policy Types | Type | What it controls | | --------------------------------------------------- | -------------------------------- | | `MAX_AMOUNT` | Per-transaction cap | | `DAILY_LIMIT` / `WEEKLY_LIMIT` / `MONTHLY_LIMIT` | Cumulative spend caps | | `ALLOWED_CATEGORIES` / `BLOCKED_CATEGORIES` | Category whitelist/blocklist | | `ALLOWED_COUNTERPARTIES` / `BLOCKED_COUNTERPARTIES` | Address whitelist/blocklist | | `TIME_WINDOW` / `DAY_OF_WEEK` | Business hours, allowed days | | `BLACKOUT_PERIOD` | Maintenance windows | | `VELOCITY_LIMIT` | Transaction rate limiting | | `REQUIRE_APPROVAL_ABOVE` | Human approval threshold | | `GEOGRAPHIC_RESTRICTION` | Country/OFAC restrictions | | `CONTRACT_ALLOWLIST` | DeFi contract restrictions | | `X402_PRICE_CEILING` | Max per x402 API call | | `X402_ALLOWED_SERVICES` / `X402_BLOCKED_SERVICES` | x402 service allowlist/blocklist | | `X402_MAX_PER_SERVICE` | Per-service daily cap | ## End-to-End Example: Pay a Vendor on Tempo Testnet This walkthrough sends a real payment through the full external-wallet flow: **approve → transfer onchain → confirm back to Conto**. ### Prerequisites - Conto account ([sign up](https://conto.finance)) with your agent connected - Conto skill installed in OpenClaw (`npx clawhub install conto`) - SDK key (`conto_agent_...`) configured in `openclaw.json` - A wallet address (see [Find Your Wallet Address](#find-your-wallet-address)) ### Step 1 — Connect to Tempo Testnet Tempo Testnet is where you'll test payments before going to production. It uses **pathUSD** as its stablecoin and requires **no gas token** — transaction fees are paid in pathUSD itself, so you don't need to acquire a separate token for gas. | Detail | Value | | -------- | ---------------------------------------------------------------- | | Network | Tempo Testnet | | Chain ID | 42431 | | Currency | pathUSD (TIP-20 stablecoin) | | Gas | Paid in pathUSD (no separate gas token) | | Explorer | [explore.moderato.tempo.xyz](https://explore.moderato.tempo.xyz) | ### Step 2 — Get testnet funds You need pathUSD in your wallet to test payments. There are a few ways to get testnet tokens: **From the Conto dashboard (Privy wallets):** If you created a Privy wallet in Conto, go to **Wallets > your wallet** and click the **Faucet** button. This deposits complimentary testnet pathUSD directly into your wallet. **From the Tempo faucet:** Visit the [Tempo testnet faucet](https://faucet.tempo.network) and enter your wallet address to receive testnet pathUSD. **From your Sponge wallet (if applicable):** If your agent uses a Sponge MCP wallet on Tempo testnet, check if you already have a balance: ``` > What's my balance on Tempo? ``` If you need funds, you can swap or bridge tokens to Tempo using your Sponge tools, or use the Tempo faucet above with your Sponge wallet address. Verify you have funds before proceeding — you'll need at least enough pathUSD to cover your test payment plus a small amount for transaction fees. ### Step 3 — Register your wallet in Conto Now connect your wallet to the agent you connected in the Conto dashboard: 1. Go to **Agents > your agent > Wallets** 2. Click **Link Wallet** 3. Enter your wallet address and select: - **Chain**: Tempo Testnet (42431) - **Custody type**: EXTERNAL (if using Sponge or your own wallet) or PRIVY (if you created one in Conto) 4. Set initial spending limits: - **Per Transaction**: 200 pathUSD - **Daily**: 1,000 pathUSD - **Weekly**: 5,000 pathUSD 5. Click **Link** These wallet-level limits act as a safety net on top of any policies you create. ### Step 4 — Create a spending policy ``` /conto create a policy that limits each transaction to 200 pathUSD ``` The skill calls `POST /api/sdk/policies` and returns the policy ID. Verify it: ``` /conto list my policies ``` ### Step 5 — Request a payment (approve) ``` Send 50 pathUSD to 0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18 on Tempo ``` Behind the scenes the skill calls: ```bash curl -X POST https://conto.finance/api/sdk/payments/approve \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 50, "recipientAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18", "senderAddress": "0x1a2b3c4d5e6f...", "chainId": 42431, "purpose": "Vendor payment" }' ``` Conto evaluates every assigned policy. If approved, the response includes an `approvalId` and `approvalToken`. ### Step 6 — Transfer onchain Because this is an external wallet, the agent transfers pathUSD itself using its own keys. The skill handles this automatically — you don't need to do anything extra. ### Step 7 — Confirm back to Conto After the onchain transfer succeeds, the skill reports the transaction hash: ```bash curl -X POST https://conto.finance/api/sdk/payments/APPROVAL_ID/confirm \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "txHash": "0xabc123...", "approvalToken": "a1b2c3d4..." }' ``` The `approvalToken` is the token from the Step 5 approval response. Conto records the payment, updates spend counters, and the transaction appears in the dashboard. ### Step 8 — Verify the result ``` /conto show my recent transactions ``` Or check the [Conto dashboard](https://conto.finance) under **Transactions** — you'll see the confirmed payment with its explorer link. ### What happens if a policy blocks the payment? Try exceeding the limit: ``` Send 300 pathUSD to 0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18 on Tempo ``` The skill returns a denial with the specific violation. No onchain transfer occurs. The denied attempt appears under **Alerts** in the dashboard. The denial response looks like this: ```json { "approved": false, "reasons": ["Amount 300 exceeds maximum of 200 per transaction"], "violations": [ { "type": "PER_TX_LIMIT", "limit": 200, "current": 300, "message": "Amount 300 exceeds maximum of 200 per transaction" } ], "requiresHumanApproval": false } ``` Common violation types: `PER_TX_LIMIT`, `DAILY_LIMIT`, `WEEKLY_LIMIT`, `MONTHLY_LIMIT`, `BLOCKED_COUNTERPARTY`, `TIME_WINDOW`, `CATEGORY_RESTRICTION`, `VELOCITY_LIMIT`. See the [Advanced Policies](https://conto.finance/docs/policies/advanced) page for the full list. --- ## Verify in Dashboard After running payments through the skill, check the [Conto dashboard](https://conto.finance): - **Transactions** -- confirmed payments with tx hashes and explorer links - **Alerts** -- denied payment attempts with violation details - **Agents** -- spend tracking (daily/weekly/monthly used) ## Run the E2E Test For automated testing against a local Conto instance: ```bash # Terminal 1 npm run dev # Terminal 2 npx tsx scripts/test-openclaw-skill.ts ``` This creates an isolated test agent with 4 policies, runs 10 tests verifying each policy type, and cleans up afterward. ## Rate Limits | Endpoint type | Limit | | ------------------------------------------------------------------ | -------------------------- | | Payment endpoints (`/approve`, `/request`, `/execute`, `/confirm`) | 60 requests/min per agent | | Read endpoints (`/wallets`, `/policies`, `/transactions`, etc.) | 120 requests/min per agent | When rate limited, the API returns `429` with a `Retry-After` header. The skill handles retries automatically. ## Troubleshooting Verify `CONTO_API_URL` in `openclaw.json` is correct. For the hosted platform, use `https://conto.finance`. For local development, use `http://localhost:3006`. Test connectivity: ```bash curl https://conto.finance/api/sdk/setup \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` If you get a valid JSON response, the URL is reachable. SDK keys are scoped to a single agent. Check that: - The key starts with `conto_agent_` (not `conto_org_`) - The key hasn't been revoked in the dashboard under **Agents > SDK Keys** - You're using the correct key for the correct agent Generate a new key if needed: **Agents > your agent > SDK Keys > Generate New Key**. The denial response includes a `violations` array listing every rule that failed. Common causes: - **Spend limit exceeded** — check daily/weekly/monthly counters in the dashboard under **Agents > Spend Tracking** - **Counterparty not on allowlist** — if you have an `ALLOWED_COUNTERPARTIES` policy, the recipient must be listed - **Outside time window** — `TIME_WINDOW` and `DAY_OF_WEEK` rules use the timezone set on the policy - **Category mismatch** — if `ALLOWED_CATEGORIES` is set and no `category` is provided in the request, the rule is skipped (not denied). But if `BLOCKED_CATEGORIES` matches, it denies. Use the dry-run check to debug without attempting a real payment: ``` /conto check if a 50 pathUSD payment to 0x742d... is allowed ``` In external wallet mode, Conto only enforces policy — the agent must transfer funds itself. If the approve call succeeds but no transfer happens: - Check the agent has enough pathUSD in its wallet - Check the OpenClaw agent logs for transfer errors - Ensure the wallet address registered in Conto matches the agent's actual wallet If the transfer succeeded but Conto doesn't show it, the confirm call may have failed. Retry: ```bash curl -X POST https://conto.finance/api/sdk/payments/APPROVAL_ID/confirm \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{"txHash": "0x...", "approvalToken": "a1b2c3d4..."}' ``` Policy management requires an **Admin** SDK key. Standard keys can only read policies and approve/confirm payments. Check the key type in the dashboard under **Agents > SDK Keys** — the scope column shows `standard` or `admin`. The `@conto/sdk` npm package is not used by the OpenClaw skill. The skill communicates with Conto through `conto-check.sh`, which calls the REST API using `curl`. To install the skill, run: ```bash npx clawhub install conto ``` Then follow the [Quick Setup](#quick-setup-recommended) steps above. If OpenClaw throws parse errors or every command fails after you manually edited `~/.openclaw/openclaw.json`, the file likely has a JSON syntax error. Common mistakes: - Trailing comma after the last key-value pair - Missing comma between key-value pairs - Mismatched braces or brackets - Placeholder text like `your_key_here` not replaced with an actual key Validate the file: ```bash cat ~/.openclaw/openclaw.json | jq . ``` If `jq` reports an error, fix the JSON or delete the file and re-run setup: ```bash rm ~/.openclaw/openclaw.json conto-check.sh setup "my-agent" "0xMyWalletAddress" EVM 42431 ``` The setup command writes a correctly formatted file automatically. Policies must be **assigned to the agent**. Creating a policy alone doesn't activate it. Assign via the dashboard (**Policies > Assign to Agent**) or via the API: ```bash curl -X POST https://conto.finance/api/agents/AGENT_ID/policies \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{"policyId": "POLICY_ID"}' ``` Also verify the policy status is `ACTIVE` (not `DRAFT` or `DISABLED`). --- # Payments API The payments API allows agents to request authorization and execute stablecoin payments. ## Overview The payment flow has two steps: 1. **Request** - Request authorization and policy evaluation 2. **Execute** - Execute the approved payment onchain Or use `autoExecute: true` to request and execute in a single API call. You can also use the SDK convenience method `pay()` to do both in one call. ## Choose The Right Flow | Wallet model | Use this flow | What Conto can stop | | ------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------- | | **Managed** (`PRIVY`, `SPONGE`) | `request -> execute` | Conto stays in the execution path and can block the spend | | **External** (`EXTERNAL`) | `approve -> transfer -> confirm` | Conto governs the Conto-routed flow, but cannot block a direct self-signed transfer outside Conto | `payments.execute()` is for managed wallets. If your agent holds the signing keys, use the external-wallet `approve -> confirm` flow instead. ## payments.request() Request authorization for a payment. This evaluates policies without executing. ```typescript const request = await conto.payments.request({ amount: 100, recipientAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f...', recipientName: 'OpenAI', purpose: 'GPT-4 API credits', category: 'AI_SERVICES', }); ``` ### Parameters | Parameter | Type | Required | Description | | ----------------------- | --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `amount` | number | Yes | Payment amount | | `recipientAddress` | string | Yes | Wallet address — `0x` + 40 hex chars for EVM, or base58 (32-44 chars) for Solana. Validated via Zod schema. | | `recipientName` | string | No | Human-readable name | | `purpose` | string | No | Why this payment is needed | | `category` | string | No | Spending category | | `context` | object | No | Additional metadata | | `walletId` | string | No | Specific wallet to use | | `urgency` | string | No | LOW, NORMAL, HIGH, CRITICAL | | `autoExecute` | `boolean` | No | If `true`, automatically execute the payment when approved. Returns the transaction result directly instead of requiring a separate `execute()` call. | | `targetContractAddress` | string | No | Smart contract address for contract interaction policy evaluation | | `functionSelector` | string | No | 4-byte function selector (e.g., `0xa9059cbb`) for contract allowlist rules | | `idempotencyKey` | string | No | Client-supplied key that makes retries safe. Reusing the same key with the same request returns the original request; reusing it with different request parameters returns a conflict. | Note: **External wallet users (OpenClaw):** If your agent controls its own wallet keys and uses the `/api/sdk/payments/approve` endpoint instead, `chainId` is a **required** parameter. See the [OpenClaw guide](https://conto.finance/docs/sdk/openclaw) for the external wallet flow. ### External Wallet Approve/Confirm Flow If your agent holds the signing keys, use the external-wallet flow instead of `payments.execute()`: 1. Call `POST /api/sdk/payments/approve` to evaluate policy and receive an `approvalToken` 2. Submit the onchain transfer through your own signer or wallet integration 3. Call `POST /api/sdk/payments/{requestId}/confirm` with the `approvalToken` and final `txHash` This keeps the same policy checks and audit trail while leaving execution in the agent's control. It does not cryptographically block a direct transfer signed outside Conto. Note: If the `senderAddress` has not been seen before, Conto auto-creates an external wallet record for that address and links it to the agent's organization. That record still counts toward the organization's wallet limit, so approval can fail with `WALLET_LIMIT_REACHED` when the org is already at capacity. ### Response ```typescript interface PaymentRequestResult { requestId: string; // Use this to execute status: 'APPROVED' | 'DENIED' | 'REQUIRES_APPROVAL' | 'EXECUTED'; idempotent?: boolean; // Present when a retry returns an existing request // If approved or executed wallet?: { id: string; address: string; chainId: string; custodyType: string; availableBalance: number; }; walletSelectionReason?: string; expiresAt?: string; // ISO timestamp currency?: string; // e.g., "USDC", "USDT", "pathUSD" chain?: { chainId: string; chainName: string; chainType: string; explorerUrl?: string; }; // If executed (autoExecute was true) execution?: { transactionId: string; txHash: string; explorerUrl: string; status: string; }; // Always present reasons: string[]; // If denied - includes enriched context violations?: { type: string; limit: number; current: number; message: string; // Prefixed with source, e.g. "[Wallet limit]" or "[Policy: Name]" source?: 'wallet_limit' | 'policy_rule'; policyName?: string; // Present when source is "policy_rule" }[]; context?: { wallets?: Array<{ id: string; address: string; chainId: string; custodyType: string; balance: number; }>; nextSteps?: string[]; }; // If autoExecute failed autoExecuteError?: string; } ``` Info: All payment responses now include `currency` (e.g., "USDC", "USDT", "USDC.e", or "pathUSD") and `chain` (with chainId, chainName, chainType, and explorerUrl) so agents know exactly which network and token the payment uses. Info: **Currency depends on chain:** The `currency` field reflects the stablecoin used on the wallet's blockchain. On Base, Ethereum, Arbitrum, and Polygon, both `USDC` and `USDT` are supported. On Tempo Testnet, it is `pathUSD`. On Tempo Mainnet, it is `USDC.e`. The amount is always denominated in the stablecoin configured for the payment. ### Idempotent Retries Use `idempotencyKey` when your caller may retry the same payment request because of network timeouts or uncertain client state. - Same `idempotencyKey` + same request payload: returns the original `requestId` with `idempotent: true` - Same `idempotencyKey` + different request payload: returns HTTP `409` with `code: "IDEMPOTENCY_CONFLICT"` - Different `idempotencyKey`: creates a new payment request ```typescript const request = await conto.payments.request({ amount: 100, recipientAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f...', purpose: 'Top up API credits', idempotencyKey: 'payment-req-2026-04-17-001', }); ``` ### Example ```typescript const request = await conto.payments.request({ amount: 50, recipientAddress: '0x...', purpose: 'API credits', }); switch (request.status) { case 'APPROVED': console.log('Payment approved'); console.log('Wallet:', request.wallet?.address); console.log('Expires:', request.expiresAt); break; case 'DENIED': console.log('Payment denied:', request.reasons); break; case 'REQUIRES_APPROVAL': console.log('Needs manual approval'); break; } ``` ## payments.execute() Execute an approved payment request. ```typescript const result = await conto.payments.execute(requestId); ``` ### Parameters | Parameter | Type | Required | Description | | ----------- | ------ | -------- | ---------------------------------- | | `requestId` | string | Yes | The requestId from payment request | ### Response ```typescript interface PaymentExecuteResult { transactionId: string; txHash: string; // Blockchain transaction hash status: 'CONFIRMING' | 'CONFIRMED' | 'FAILED'; amount: number; currency: string; // Stablecoin type recipient: string; recipientName?: string; wallet: { address: string; }; explorerUrl: string; // Block explorer link } ``` ### Example ```typescript const request = await conto.payments.request({ amount: 50, recipientAddress: '0x...', }); if (request.status === 'APPROVED') { const result = await conto.payments.execute(request.requestId); console.log('Transaction hash:', result.txHash); console.log('Explorer:', result.explorerUrl); } ``` ## payments.pay() Convenience method that requests and executes in one call. ```typescript const result = await conto.payments.pay({ amount: 50, recipientAddress: '0x...', purpose: 'API credits', }); ``` ### Behavior - If **approved**: Executes immediately and returns result - If **denied**: Throws `ContoError` with code `PAYMENT_DENIED` - If **requires approval**: Throws `ContoError` with code `REQUIRES_APPROVAL` ### Example ```typescript try { const result = await conto.payments.pay({ amount: 50, recipientAddress: '0x...', purpose: 'API credits', }); console.log('Paid! TX:', result.txHash); } catch (error) { if (error.code === 'PAYMENT_DENIED') { console.log('Payment denied:', error.message); } else if (error.code === 'REQUIRES_APPROVAL') { console.log('Payment needs manual approval'); } } ``` ## autoExecute Flag The `autoExecute` flag lets you request authorization and execute the payment in a single API call, without needing a separate `execute()` call. Requires both `payments:request` and `payments:execute` scopes. Since `payments:execute` is **not included in default scopes**, you must explicitly grant it when creating the SDK key. If the key only has `payments:request`, the flag is silently ignored and the response is a normal APPROVED status. ### How It Works - If **APPROVED** + `autoExecute: true`: Executes immediately, returns `status: "EXECUTED"` with `execution` object containing `txHash` - If **DENIED** or **REQUIRES_APPROVAL**: `autoExecute` is ignored, normal response returned - If execution **fails**: Returns `status: "APPROVED"` with `autoExecuteError` message and `executeUrl` for manual retry ### Example ```typescript // Single-call payment: request + execute const result = await fetch('/api/sdk/payments/request', { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ amount: 50, recipientAddress: '0x...', recipientName: 'OpenAI', purpose: 'API credits', autoExecute: true }) }).then(r => r.json()); if (result.status === 'EXECUTED') { console.log('Payment complete!'); console.log('TX Hash:', result.execution.txHash); console.log('Explorer:', result.execution.explorerUrl); } else if (result.status === 'APPROVED' && result.autoExecuteError) { // Auto-execute failed, retry manually console.log('Auto-execute failed:', result.autoExecuteError); const execResult = await fetch(result.executeUrl, { method: 'POST', ... }); } ``` ## payments.status() Check the status of a payment request. ```typescript const status = await conto.payments.status(requestId); ``` ### Response ```typescript interface PaymentStatusResult { requestId: string; status: 'PENDING' | 'APPROVED' | 'DENIED' | 'COMPLETED' | 'EXPIRED'; policyResult: string; amount: number; currency: string; recipient: string; recipientName?: string; purpose?: string; category?: string; wallet?: { address: string }; requiresApproval: boolean; approvedAt?: string; deniedAt?: string; denialReason?: string; expiresAt?: string; createdAt: string; // If executed transaction?: { id: string; txHash: string; status: 'PENDING' | 'CONFIRMING' | 'CONFIRMED' | 'FAILED'; confirmedAt?: string; blockNumber?: number; }; } ``` ### Example: Polling for Confirmation ```typescript async function waitForConfirmation(requestId: string) { while (true) { const status = await conto.payments.status(requestId); if (status.transaction?.status === 'CONFIRMED') { console.log('Confirmed at block:', status.transaction.blockNumber); return status; } if (status.transaction?.status === 'FAILED') { throw new Error('Transaction failed'); } await new Promise((r) => setTimeout(r, 2000)); // Wait 2 seconds } } ``` ## Status Reference Payment requests and transactions use different status values: | Payment Request Status | Description | | ---------------------- | ------------------------------------------------------------------- | | `APPROVED` | Policies passed, ready to execute | | `DENIED` | Blocked by a policy or wallet limit | | `REQUIRES_APPROVAL` | Needs manual human approval | | `EXECUTED` | Auto-executed successfully (when `autoExecute: true`) | | `PENDING` | Awaiting human approval (maps to REQUIRES_APPROVAL in SDK response) | | `COMPLETED` | Payment fully confirmed onchain | | `EXPIRED` | Approval window elapsed without execution | | Transaction Status | Description | | ------------------ | -------------------------------------------------- | | `PENDING` | Transaction submitted, awaiting confirmation | | `EXECUTING` | Transaction is being processed | | `CONFIRMING` | Transaction broadcast, awaiting block confirmation | | `CONFIRMED` | Transaction confirmed onchain | | `FAILED` | Transaction failed | | `REJECTED` | Transaction rejected | | `CANCELLED` | Transaction cancelled before execution | | Policy Result | Description | | ------------------- | ------------------------------------------------ | | `ALLOWED` | All policies passed | | `DENIED` | At least one policy denied the payment | | `REQUIRES_APPROVAL` | Policies passed but approval threshold triggered | | `FLAGGED` | Payment flagged for review | | `PENDING` | Policy evaluation not yet complete | ## Categories Use standard categories for better analytics: | Category | Description | | ---------------- | ----------------------- | | `INFRASTRUCTURE` | Cloud, hosting, compute | | `AI_SERVICES` | AI APIs, model training | | `MARKETING` | Advertising, promotions | | `OPERATIONS` | General operations | | `VENDOR` | Vendor payments | | `EMPLOYEE` | Employee reimbursements | | `TESTING` | Test transactions | ## Urgency Levels | Level | Description | | ---------- | --------------------------- | | `LOW` | Can wait, batch if possible | | `NORMAL` | Standard priority (default) | | `HIGH` | Process quickly | | `CRITICAL` | Immediate processing | ## Best Practices Including purpose improves audit trails and analytics: ```typescript await conto.payments.pay({ amount: 100, recipientAddress: '0x...', purpose: 'AWS EC2 instance for training job #1234', // Specific category: 'INFRASTRUCTURE' }); ``` Approvals from `/request` expire after **5 minutes**. Approvals from `/approve` (external wallets) expire after **10 minutes**. Check expiration before executing: ```typescript const request = await conto.payments.request({ ... }); if (request.status === 'APPROVED') { const expiresAt = new Date(request.expiresAt!); if (expiresAt > new Date()) { await conto.payments.execute(request.requestId); } else { // Request a new approval const newRequest = await conto.payments.request({ ... }); } } ``` Use separate request/execute when you need to: - Validate before executing - Show user confirmation - Handle requires_approval status ```typescript const request = await conto.payments.request({ ... }); if (request.status === 'REQUIRES_APPROVAL') { // Store requestId, notify approvers await notifyApprovers(request.requestId); return { pending: true, requestId: request.requestId }; } if (request.status === 'APPROVED') { return conto.payments.execute(request.requestId); } ``` Use the context field for debugging info: ```typescript await conto.payments.pay({ amount: 100, recipientAddress: '0x...', purpose: 'API subscription', context: { jobId: '1234', userId: 'user_abc', environment: 'production' } }); ``` ## Next Steps ### Error Handling Link: https://conto.finance/docs/sdk/error-handling Handle payment errors gracefully ### Examples Link: https://conto.finance/docs/sdk/examples See complete integration examples --- # x402 Protocol Payments Conto integrates with the [x402 protocol](https://www.x402.org/) to let AI agents pay for HTTP APIs that return `402 Payment Required`. Conto acts as a policy layer between your agent and the x402 facilitator. ## How It Works ``` Agent calls API → Gets 402 response → Conto pre-authorizes → Agent pays & retries ``` 1. Agent sends a request to an x402-enabled API 2. API returns HTTP 402 with payment details (amount, recipient, facilitator) 3. Agent calls Conto to pre-authorize the payment against policies 4. If approved, agent signs the payment and retries the API call 5. Agent records the transaction in Conto for tracking ## Pre-Authorization Before making an x402 payment, check it against your policies and budget limits: ```bash curl -X POST https://conto.finance/api/sdk/x402/pre-authorize \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 0.05, "recipientAddress": "0xFacilitatorAddress", "resourceUrl": "https://api.example.com/data", "facilitator": "0xFacilitatorAddress", "scheme": "exact" }' ``` Conto derives `serviceDomain` from `resourceUrl`, so you do not need to send it separately. **Response (Approved):** ```json { "authorized": true, "wallet": { "id": "wal_123", "address": "0xAgentWallet", "chainId": "8453", "availableBalance": 500.0 }, "reasons": ["Within x402 service budget", "Service domain allowed"] } ``` **Response (Denied):** ```json { "authorized": false, "reasons": ["X402 price ceiling exceeded: $0.05 > $0.01 max"], "violations": [ { "type": "X402_PRICE_CEILING", "limit": 0.01, "current": 0.05, "message": "Amount exceeds x402 price ceiling" } ] } ``` ## Recording Transactions After the x402 payment is executed onchain, record it in Conto: ```bash curl -X POST https://conto.finance/api/sdk/x402/record \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 0.05, "recipientAddress": "0xFacilitatorAddress", "txHash": "0xabc123...", "resourceUrl": "https://api.example.com/data", "facilitator": "0xFacilitatorAddress", "scheme": "exact", "walletId": "wal_123", "chainId": "8453" }' ``` ### Batch Recording For high-frequency micropayments, batch multiple records: ```bash curl -X POST https://conto.finance/api/sdk/x402/record \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 0.03, "recipientAddress": "0xFacilitatorAddress", "txHash": "0xsettlement123...", "resourceUrl": "https://api.example.com/batch", "batchItems": [ { "amount": 0.01, "resourceUrl": "https://api.example.com/v1", "paymentId": "pay_1", "responseCode": 200 }, { "amount": 0.02, "resourceUrl": "https://api.example.com/v2", "paymentId": "pay_2", "responseCode": 200 } ] }' ``` ## Querying Services View which x402 services your agent has used: ```bash GET /api/sdk/x402/services ``` ## Budget Tracking Check remaining budget and burn rate: ```bash GET /api/sdk/x402/budget ``` If an agent has an active approved budget request, `/pre-authorize` returns a `budgetRequest` snapshot and `/record` decrements the remaining budget automatically. ## Unified Machine Spend View If this agent also uses MPP or multiple paid services, use the shared machine-spend endpoints for a combined view: ```bash GET /api/sdk/service-spend/summary?protocol=x402 GET /api/sdk/service-spend/services?protocol=x402 ``` See [Machine Spend](https://conto.finance/docs/sdk/machine-spend) for the combined x402 + MPP analytics view. ## x402 Policy Rules Configure x402-specific policies to control micropayment behavior. See [Advanced Policies](https://conto.finance/docs/policies/advanced) for the full rule type reference. Key rule types: - `X402_MAX_PER_REQUEST` — Cap per individual request - `X402_PRICE_CEILING` — Hard price ceiling - `X402_MAX_PER_SERVICE` — Budget per service domain - `X402_ALLOWED_SERVICES` — Allowlist of service domains - `X402_BLOCKED_SERVICES` — Blocklist of service domains - `X402_SESSION_BUDGET` — Session-level budget cap ## Anomaly Detection Conto automatically monitors x402 spending patterns and creates alerts for: - Price spikes (sudden cost increases) - High-frequency calls (unusual request rates) - New services (first-time service usage) - Budget burn rate (approaching limits) - Duplicate payments - Failed streaks (repeated failures) ## Next Steps ### Advanced Policies Link: https://conto.finance/docs/policies/advanced Configure x402-specific policy rules ### Machine Spend Link: https://conto.finance/docs/sdk/machine-spend View unified x402 and MPP service spend ### Error Handling Link: https://conto.finance/docs/sdk/error-handling Handle x402 authorization errors --- ## Prerequisites - [Claude Code](https://docs.anthropic.com/en/docs/claude-code) installed - A Conto agent API key ([get one here](https://conto.finance)) ## Setup Run a single command to add Conto as an MCP server: ```bash claude mcp add conto -- npx @conto_finance/mcp-server \ --env CONTO_API_KEY=conto_agent_xxx... \ --env CONTO_BASE_URL=https://conto.finance ``` Replace `conto_agent_xxx...` with your actual API key. ## Verify Start a new Claude Code session and try: ``` > What are my wallet balances? ``` Claude will use the `get_wallets` tool to fetch your agent's wallet information. ## Managing the Server List configured MCP servers: ```bash claude mcp list ``` Remove the Conto server: ```bash claude mcp remove conto ``` --- ## Prerequisites - [Claude Desktop](https://claude.ai/download) installed - A Conto agent API key ([get one here](https://conto.finance)) ## Configuration Add the following to your `claude_desktop_config.json`: File location: `~/Library/Application Support/Claude/claude_desktop_config.json` ```json { "mcpServers": { "conto": { "command": "npx", "args": ["@conto_finance/mcp-server"], "env": { "CONTO_API_KEY": "conto_agent_xxx...", "CONTO_BASE_URL": "https://conto.finance" } } } } ``` File location: `%APPDATA%\Claude\claude_desktop_config.json` ```json { "mcpServers": { "conto": { "command": "npx", "args": ["@conto_finance/mcp-server"], "env": { "CONTO_API_KEY": "conto_agent_xxx...", "CONTO_BASE_URL": "https://conto.finance" } } } } ``` Replace `conto_agent_xxx...` with your actual SDK key. ## Verify 1. Restart Claude Desktop 2. Look for the tools icon in the chat input — you should see Conto's 47 tools listed 3. Try asking Claude: "What are my wallet balances?" --- ## What is MCP? MCP (Model Context Protocol) is an open protocol that allows AI agents to connect to external tools and data sources. Conto's MCP server gives Claude and other MCP-compatible agents the ability to manage wallets, make payments, track transactions, and query analytics, with custody-aware policy controls across managed and external-wallet flows. ## Package Install from npm: ```bash npm install @conto_finance/mcp-server ``` Or run directly with npx: ```bash npx @conto_finance/mcp-server ``` ## Quick Start Choose your setup method: ### Claude Desktop Link: https://conto.finance/docs/mcp/claude-desktop Add to your Claude Desktop configuration file ### Claude Code (CLI) Link: https://conto.finance/docs/mcp/claude-code One command to add Conto to Claude Code ## Getting an API Key 1. Sign in to [Conto](https://conto.finance) 2. Go to **Agents** and create or select an agent 3. Generate an SDK key from the agent's detail page 4. Use the key as `CONTO_API_KEY` ## How It Works 1. **User requests an action** — A user asks Claude to make a payment, check balances, or perform another financial operation in natural language. 2. **Policy evaluation** — Claude calls Conto MCP tools. The server evaluates the request against your organization's spending policies (limits, time windows, counterparty rules, etc.). 3. **Execution** — If approved, the action is executed onchain and Claude reports the result with transaction details. ## Environment Variables | Variable | Required | Default | Description | | ---------------- | -------- | ----------------------- | ---------------------------------- | | `CONTO_API_KEY` | Yes | — | Agent SDK key from Conto dashboard | | `CONTO_BASE_URL` | No | `https://conto.finance` | Conto API base URL | ## Available Tools The MCP server exposes **47 tools** across 11 categories. See the [full tools reference](https://conto.finance/docs/mcp/tools) for details. | Category | Tools | Description | | -------------------- | ----- | ---------------------------------------------- | | Payments | 6 | Request, execute, and monitor payments | | Wallets & Limits | 3 | Query balances and spending limits | | Transactions | 3 | List and inspect transaction history | | x402 Protocol | 4 | Pre-authorize and track x402 API micropayments | | MPP Protocol | 4 | Pre-authorize and track MPP payments | | Card Payments | 2 | Approve and confirm card transactions | | Agent-to-Agent | 6 | Send and manage inter-agent payments | | Trust & Intelligence | 4 | Check trust scores and manage counterparties | | Monitoring | 4 | Manage alerts and approval requests | | Analytics & Info | 7 | Spending analytics, policies, and agent data | | Audit & Rate Limits | 2 | Audit logs and API usage stats | --- Info: All MCP server API requests have a **30-second timeout** and all user-supplied parameters are sanitized before URL interpolation to prevent path injection. ## Payments (6) | Tool | Description | | -------------------------- | ------------------------------------------------------------ | | `pay` | Request and execute a payment in one step | | `request_payment` | Request payment authorization (check policies first) | | `execute_payment` | Execute a previously approved payment | | `check_payment_status` | Check status of a payment request | | `approve_external_payment` | Request approval for an external (agent-held) wallet payment | | `confirm_external_payment` | Confirm an externally-executed payment with tx hash | ### pay Request and execute a payment in one step. Combines `request_payment` + `execute_payment`. Fails if the payment is denied or requires manual approval. **Parameters:** | Parameter | Type | Required | Description | | ------------------ | ------ | -------- | ----------------------------- | | `amount` | number | Yes | Amount in USDC | | `recipientAddress` | string | Yes | Recipient wallet address | | `recipientName` | string | No | Human-readable recipient name | | `purpose` | string | No | Why this payment is needed | | `category` | string | No | Spend category for analytics | | `sessionId` | string | No | Session/correlation ID | ### request_payment Request authorization for a payment. Evaluates spending policies and returns whether the payment is approved, denied, or requires manual approval. **Parameters:** Same as `pay`. **Returns:** `requestId`, `status` (`APPROVED` / `DENIED` / `REQUIRES_APPROVAL`), `wallet`, `reasons`, and `violations`. ### execute_payment Execute a previously approved payment request. | Parameter | Type | Required | Description | | ----------- | ------ | -------- | ------------------------------------ | | `requestId` | string | Yes | The requestId from `request_payment` | ### approve_external_payment Request approval for a payment from an external (agent-held) wallet. | Parameter | Type | Required | Description | | ------------------ | ------ | -------- | ------------------------------------------------------ | | `amount` | number | Yes | Amount in USDC | | `recipientAddress` | string | Yes | Recipient wallet address | | `senderAddress` | string | Yes | Sender wallet address (agent-held) | | `recipientName` | string | No | Recipient name | | `purpose` | string | No | Payment purpose | | `category` | string | No | Spend category | | `chainId` | number | Yes | Chain ID (e.g. 8453 for Base, 42431 for Tempo Testnet) | ### confirm_external_payment Confirm an externally-executed payment was completed onchain. | Parameter | Type | Required | Description | | --------------- | ------ | -------- | ---------------------------------------------- | | `requestId` | string | Yes | Payment request ID from approval | | `txHash` | string | Yes | Onchain transaction hash | | `approvalToken` | string | Yes | Approval token from `approve_external_payment` | --- ## Wallets & Limits (3) | Tool | Description | | --------------------- | -------------------------------------------------- | | `get_wallets` | List all wallets linked to this agent | | `get_wallet` | Get details of a specific wallet | | `get_spending_limits` | Get current spending limits and remaining balances | ### get_wallet | Parameter | Type | Required | Description | | ---------- | ------ | -------- | ----------- | | `walletId` | string | Yes | Wallet ID | --- ## Budget Requests (2) | Tool | Description | | -------------------- | ------------------------------------------------- | | `request_budget` | Request a spending budget for human approval | | `get_budget_request` | Check budget request status and remaining balance | ### request_budget Request a spending budget for payments. Submits a request for human approval. Only one active budget request per agent at a time. Applies to all payment types (standard, x402, MPP). | Parameter | Type | Required | Description | | ---------- | ------ | -------- | --------------------------------------------------- | | `amount` | number | Yes | Requested budget in USDC | | `purpose` | string | Yes | What this budget is for (shown to reviewer) | | `category` | string | No | Spend category (e.g., API_PROVIDER, INFRASTRUCTURE) | | `metadata` | object | No | Additional context | ### get_budget_request Check status and remaining balance of your budget requests. Use to poll for approval after submitting a request, and to monitor remaining budget during spending. | Parameter | Type | Required | Description | | ----------------- | ------ | -------- | ------------------------------------------------------- | | `budgetRequestId` | string | No | Specific budget request ID | | `status` | string | No | Filter: PENDING, APPROVED, REJECTED, EXPIRED, EXHAUSTED | --- ## Transactions (3) | Tool | Description | | ------------------- | --------------------------------------- | | `list_transactions` | List transactions with optional filters | | `get_transaction` | Get details of a specific transaction | | `retry_transaction` | Retry a failed transaction | ### list_transactions | Parameter | Type | Required | Description | | --------- | ------ | -------- | ---------------------------------- | | `status` | string | No | Filter: PENDING, CONFIRMED, FAILED | | `from` | string | No | Start date (ISO string) | | `to` | string | No | End date (ISO string) | | `limit` | number | No | Results per page (max 100) | | `offset` | number | No | Pagination offset | --- ## x402 Protocol (4) | Tool | Description | | -------------------- | -------------------------------------------------- | | `x402_get_budget` | Check remaining x402 budget and burn rate | | `x402_pre_authorize` | Pre-authorize an x402 API payment against policies | | `x402_record` | Record a completed x402 payment (single or batch) | | `x402_list_services` | List x402 services with spend and pricing stats | ### x402_pre_authorize | Parameter | Type | Required | Description | | ------------------ | ------ | -------- | ----------------------------- | | `amount` | number | Yes | Payment amount | | `recipientAddress` | string | Yes | Facilitator/recipient address | | `resourceUrl` | string | Yes | URL of the API endpoint | | `facilitator` | string | No | x402 facilitator address | | `walletId` | string | No | Preferred wallet ID | | `sessionId` | string | No | Session ID for grouping calls | | `category` | string | No | Spend category | `serviceDomain` is derived automatically from `resourceUrl`. ### x402_record | Parameter | Type | Required | Description | | ------------------ | ------ | -------- | ------------------------------------------ | | `amount` | number | Yes | Payment amount | | `recipientAddress` | string | Yes | Recipient address | | `resourceUrl` | string | Yes | API endpoint URL | | `txHash` | string | No | Onchain transaction hash | | `batchItems` | array | No | Array of micropayments for batch recording | ### x402_list_services | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------------------------------------------ | | `period` | string | No | Time period: '24h', '7d', '30d', '90d' (default: '7d') | | `limit` | number | No | Max results (default: 50) | --- ## MPP Protocol (4) | Tool | Description | | ------------------- | ------------------------------------------------ | | `mpp_get_budget` | Check remaining MPP budget and burn rate | | `mpp_pre_authorize` | Pre-authorize an MPP payment against policies | | `mpp_record` | Record a completed MPP payment (single or batch) | | `mpp_list_services` | List MPP services with spend and pricing stats | ### mpp_pre_authorize | Parameter | Type | Required | Description | | ------------------ | ------ | -------- | --------------------- | | `amount` | number | Yes | Payment amount | | `recipientAddress` | string | Yes | Recipient address | | `resourceUrl` | string | Yes | API endpoint URL | | `walletId` | string | No | Preferred wallet ID | | `intent` | string | No | 'charge' or 'session' | | `sessionId` | string | No | Session ID | | `paymentMethod` | string | No | Payment method | | `depositAmount` | number | No | Deposit for session intent | | `category` | string | No | Spend category | ### mpp_record | Parameter | Type | Required | Description | | ------------------ | ------ | -------- | ----------------------------------------- | | `amount` | number | Yes | Aggregate payment amount | | `recipientAddress` | string | Yes | Recipient address | | `resourceUrl` | string | Yes | API endpoint URL | | `txHash` | string | No | Onchain settlement transaction hash | | `credentialId` | string | No | MPP credential ID | | `paymentReceipt` | string | No | Payment receipt payload | | `batchItems` | array | No | Per-call detail records for batch settlement | --- ## Card Payments (2) | Tool | Description | | -------------- | ---------------------------------------------------- | | `card_approve` | Request approval for a card payment against policies | | `card_confirm` | Confirm a card payment was completed | ### card_approve | Parameter | Type | Required | Description | | ------------------ | ------ | -------- | ------------------------------ | | `cardId` | string | Yes | Card ID | | `amount` | number | Yes | Transaction amount | | `currency` | string | No | Currency code (default: "USD") | | `merchantName` | string | No | Merchant name | | `merchantCategory` | string | No | MCC code | | `purpose` | string | No | Purchase purpose | ### card_confirm | Parameter | Type | Required | Description | | --------------- | ------ | -------- | -------------------------------------- | | `requestId` | string | Yes | Payment request ID from `card_approve` | | `approvalToken` | string | Yes | Approval token from `card_approve` | | `externalTxId` | string | No | External transaction ID | | `authorizationCode` | string | No | Authorization code from card processor | | `actualAmount` | number | No | Actual charged amount | --- ## Agent-to-Agent (6) | Tool | Description | | --------------------- | -------------------------------------------- | | `a2a_send_request` | Send a payment request to another agent | | `a2a_list_requests` | List incoming/outgoing A2A requests | | `a2a_respond` | Approve or reject an A2A request | | `a2a_execute` | Execute an approved A2A payment | | `a2a_resolve_address` | Check if an address belongs to a Conto agent | | `a2a_get_stats` | Get A2A payment statistics | ### a2a_send_request | Parameter | Type | Required | Description | | --------------------- | ------ | -------- | -------------------------------------------- | | `amount` | number | Yes | Amount in USDC | | `targetAgentId` | string | No | Target agent ID (or use targetWalletAddress) | | `targetWalletAddress` | string | No | Target wallet address | | `purpose` | string | No | Why this payment is requested | | `invoiceId` | string | No | Invoice reference ID | ### a2a_respond | Parameter | Type | Required | Description | | ----------- | ------ | -------- | ------------------------- | | `requestId` | string | Yes | A2A request ID | | `action` | string | Yes | `'approve'` or `'reject'` | | `reason` | string | No | Optional rejection reason | --- ## Trust & Intelligence (4) | Tool | Description | | --------------------- | -------------------------------------------------- | | `check_address_trust` | Get trust score and risk info for a wallet address | | `list_counterparties` | List known counterparties with trust levels | | `get_counterparty` | Get counterparty details and transaction history | | `create_counterparty` | Create or update a counterparty | ### create_counterparty | Parameter | Type | Required | Description | | ------------- | ------ | -------- | ------------------------ | | `name` | string | Yes | Counterparty name | | `address` | string | Yes | Wallet address | | `type` | string | No | Type (default: 'VENDOR') | | `category` | string | No | Category | | `description` | string | No | Description | --- ## Monitoring (4) | Tool | Description | | ----------------------- | ------------------------------------ | | `list_alerts` | List active alerts and notifications | | `get_alert` | Get details of a specific alert | | `respond_to_alert` | Acknowledge or resolve an alert | | `get_approval_requests` | List pending approval requests | ### list_alerts | Parameter | Type | Required | Description | | ---------- | ------ | -------- | -------------------------------------------- | | `status` | string | No | Filter: `ACTIVE`, `ACKNOWLEDGED`, `RESOLVED` | | `severity` | string | No | Filter: `LOW`, `MEDIUM`, `HIGH`, `CRITICAL` | | `limit` | number | No | Results per page (max 100) | | `offset` | number | No | Pagination offset | ### get_alert | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------- | | `alertId` | string | Yes | Alert ID | ### respond_to_alert | Parameter | Type | Required | Description | | ------------ | ------ | -------- | -------------------------- | | `alertId` | string | Yes | Alert ID | | `action` | string | Yes | 'acknowledge' or 'resolve' | | `resolution` | string | No | Resolution notes | --- ## Analytics & Info (7) | Tool | Description | | -------------------------- | --------------------------------------------- | | `get_analytics` | Get spending analytics and trends | | `get_policies` | List spending policies assigned to this agent | | `request_policy_exception` | Request an exception to a spending policy | | `list_policy_exceptions` | List policy exception requests | | `get_agent_info` | Get information about this agent | | `get_all` | Get comprehensive agent data in one call | | `get_setup` | Get agent setup and configuration | ### request_policy_exception | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------------------------------------------------------------------------------- | | `type` | string | Yes | ADD_TO_WHITELIST, INCREASE_SPEND_LIMIT, EXTEND_TIME_WINDOW, ADD_CATEGORY, or CUSTOM | | `reason` | string | Yes | Why this exception is needed | | `urgency` | string | No | LOW, NORMAL, HIGH, or CRITICAL | | `details` | object | No | Exception details (address, limit, etc.) | ### get_all | Parameter | Type | Required | Description | | ----------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------- | | `include` | string | No | Comma-separated sections: agent, wallets, policies, counterparties, transactions, alerts, analytics, capabilities, endpoints | | `analyticsPeriod` | string | No | 'day', 'week', 'month', or 'year' | --- ## Audit & Rate Limits (2) | Tool | Description | | ----------------- | ------------------------------------------- | | `get_audit_logs` | Get audit logs of agent actions | | `get_rate_limits` | Get current API rate limit status and usage | ### get_audit_logs | Parameter | Type | Required | Description | | ---------- | ------ | -------- | -------------------------- | | `action` | string | No | Filter by action type | | `resource` | string | No | Filter by resource type | | `from` | string | No | Start date (ISO string) | | `to` | string | No | End date (ISO string) | | `limit` | number | No | Results per page (max 100) | | `offset` | number | No | Pagination offset | --- # Advanced Policies Advanced policy types and the rule engine for complex business requirements. ## Policy Rules Each policy consists of one or more **rules**. Rules define specific conditions and actions: ### Rule Structure ```json { "ruleType": "SINGLE_AMOUNT", "operator": "GREATER_THAN", "value": "{\"amount\": 500}", "action": "REQUIRE_APPROVAL" } ``` | Field | Description | | ---------- | -------------------------------------- | | `ruleType` | Type of condition (see below) | | `operator` | Comparison operator | | `value` | JSON-encoded value for comparison | | `action` | `ALLOW`, `DENY`, or `REQUIRE_APPROVAL` | ### Supported Rule Types Each rule type below maps to a dedicated strategy module in the policy engine. The engine dispatches evaluation through a registry, so adding a new rule type means adding a new strategy file rather than modifying core engine code. | Rule Type | Description | Example Value | | ------------------------ | --------------------------------- | -------------------------------------------------------------------------------------------- | | `MAX_AMOUNT` | Per-transaction amount | `"500"` | | `DAILY_LIMIT` | Daily spend cap | `"1000"` | | `WEEKLY_LIMIT` | Weekly spend cap | `"5000"` | | `MONTHLY_LIMIT` | Monthly spend cap | `"20000"` | | `BUDGET_CAP` | Budget allocation | `"{\"amount\": 50000, \"period\": \"MONTHLY\"}"` | | `TIME_WINDOW` | Time window restriction | `"{\"start\": \"09:00\", \"end\": \"18:00\"}"` | | `DAY_OF_WEEK` | Day restriction | `"[\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\"]"` | | `DATE_RANGE` | Temporal validity | `"{\"start\": \"2025-07-01\", \"end\": \"2025-09-30\"}"` | | `BLACKOUT_PERIOD` | Block during windows | `"{\"windows\": [{\"start\": \"02:00\", \"end\": \"06:00\", \"reason\": \"Maintenance\"}]}"` | | `ALLOWED_CATEGORIES` | Allowed categories | `"[\"software\", \"infrastructure\"]"` | | `BLOCKED_CATEGORIES` | Blocked categories | `"[\"gambling\", \"adult\"]"` | | `ALLOWED_COUNTERPARTIES` | Allowed addresses | `"[\"0x123...\", \"0x456...\"]"` | | `BLOCKED_COUNTERPARTIES` | Blocked addresses | `"[\"0xdead...\"]"` | | `VELOCITY_LIMIT` | Transaction frequency | `"{\"maxCount\": 10, \"period\": \"HOUR\"}"` | | `REQUIRE_APPROVAL_ABOVE` | Approval threshold | `"2500"` | | `GEOGRAPHIC_RESTRICTION` | Blocked countries | `"[\"CU\", \"IR\", \"KP\", \"SY\", \"RU\"]"` | | `TRUST_SCORE` | Min trust score | `"70"` | | `COUNTERPARTY_STATUS` | Trust level requirement | `"{\"status\": \"TRUSTED\"}"` | | `CONTRACT_ALLOWLIST` | Allowed contracts | `"{\"contracts\": [\"0x...\"], \"protocols\": [\"uniswap\"]}"` | | `FAIRSCALE_MIN_SCORE` | Min Fairscale reputation (Solana) | `"50"` | ### Supported Operators | Operator | Aliases | Description | | ----------------------- | ------------------------------- | ----------------- | | `EQUALS` | `EQ` | Exact match | | `NOT_EQUALS` | `NEQ` | Not equal | | `GREATER_THAN` | `GT` | Greater than | | `GREATER_THAN_OR_EQUAL` | `GTE`, `GREATER_THAN_OR_EQUALS` | Greater or equal | | `LESS_THAN` | `LT` | Less than | | `LESS_THAN_OR_EQUAL` | `LTE`, `LESS_THAN_OR_EQUALS` | Less or equal | | `IN` | `IN_LIST` | Value in list | | `NOT_IN` | `NOT_IN_LIST` | Value not in list | | `BETWEEN` | - | Within range | | `NOT_BETWEEN` | - | Outside range | ### Rule Actions - **`ALLOW`** - If condition matches, allow the transaction - **`DENY`** - If condition matches, block the transaction - **`REQUIRE_APPROVAL`** - If condition matches, require manual approval ### Example: High-Value Approval Rule Block transactions over $500 unless manually approved: ```json { "name": "High Value Transaction Review", "policyType": "APPROVAL_THRESHOLD", "priority": 80, "rules": [ { "ruleType": "SINGLE_AMOUNT", "operator": "GREATER_THAN", "value": "{\"amount\": 500}", "action": "REQUIRE_APPROVAL" } ] } ``` ## Geographic Restrictions (OFAC) Geographic restrictions include both **built-in enforcement** (always active) and **configurable rules** (via policies). ### Built-in Enforcement The policy evaluator automatically performs two checks before any other policy is evaluated: 1. **Country check** — If `recipientCountry` is provided in the payment context, it's checked against the OFAC sanctioned countries list. Blocked immediately if matched. 2. **Address sanctions screening** — The recipient address is screened against sanctions lists via the configured provider (Chainalysis, TRM Labs, or a local OFAC SDN list). Blocked immediately if flagged. Configure the sanctions provider via the `SANCTIONS_PROVIDER` environment variable: - `chainalysis` — Chainalysis Risk API (requires `CHAINALYSIS_API_KEY`) - `trm` — TRM Labs API (requires `TRM_API_KEY`) - `local` — Local OFAC SDN list (default, no API key needed) ### Local Provider Lifecycle When `SANCTIONS_PROVIDER=local`, Conto checks recipient addresses against the `SanctionedAddress` table in the application database rather than a hardcoded sample list. - The `20260506000000_add_sanctioned_address` migration seeds the table with known high-confidence matches so new deployments are never empty. - The `sanctions-list-refresh` scheduled job refreshes the table from configured OFAC feeds and records refresh metadata for auditability. - If upstream feeds are temporarily unavailable, the seeded dataset remains in place so sanctions screening still has a conservative baseline. Info: To enable country-based blocking, include `recipientCountry` (ISO 3166-1 alpha-2, e.g., `"US"`, `"IR"`) in the `PaymentContext` when calling the evaluator. The SDK payment endpoints populate this automatically when available. ### Configurable Rules Add custom country allow/deny lists on top of the built-in checks: ```bash curl -X POST https://conto.finance/api/policies/{policyId}/rules \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{ "rules": [ { "ruleType": "GEOGRAPHIC_RESTRICTION", "operator": "IN_LIST", "value": "[\"US\", \"GB\", \"DE\", \"FR\"]", "action": "ALLOW" } ] }' ``` ### OFAC Sanctioned Countries (Built-in) | Code | Country | | ---- | ----------- | | CU | Cuba | | IR | Iran | | KP | North Korea | | SY | Syria | | RU | Russia | | BY | Belarus | | MM | Myanmar | | VE | Venezuela | | SD | Sudan | | SS | South Sudan | | LB | Lebanon | | LY | Libya | | SO | Somalia | | YE | Yemen | Always consult legal counsel for compliance requirements. This is not legal advice. ## Approval Thresholds Require manual approval above certain amounts: ```json { "policyType": "APPROVAL_THRESHOLD", "rules": [ { "type": "AMOUNT_THRESHOLD", "threshold": 500, "approvers": ["admin", "finance"] } ] } ``` When triggered: - Payment returns `REQUIRES_APPROVAL` - Approvers are notified - Payment held until approved or denied Conto records only one final approval decision for each request, even if multiple approvers act at nearly the same time. This prevents the same payment from being resolved twice. For external channels, email approvals use per-user confirmation links. Shared channels such as Slack, Telegram, WhatsApp, and generic webhooks deep-link back to the dashboard until the acting approver can be verified reliably. ## Velocity Limits Control transaction frequency: ```bash curl -X POST https://conto.finance/api/policies/{policyId}/rules \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{ "rules": [ { "ruleType": "VELOCITY_LIMIT", "operator": "LESS_THAN", "value": "{\"maxCount\": 10, \"period\": \"HOUR\"}", "action": "ALLOW" }, { "ruleType": "VELOCITY_LIMIT", "operator": "LESS_THAN", "value": "{\"maxAmount\": 1000, \"period\": \"DAILY\"}", "action": "ALLOW" } ] }' ``` Note: Velocity counting is **per-wallet, per-agent**. It counts transactions with status CONFIRMED or PENDING. The current transaction is included in the count (+1). Use cases: - Prevent rapid drain attacks - Detect unusual patterns - Rate limit agent activity ## Category Restrictions Allow or block specific categories: ```bash curl -X POST https://conto.finance/api/policies/{policyId}/rules \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{ "rules": [ { "ruleType": "BLOCKED_CATEGORIES", "operator": "IN_LIST", "value": "[\"gambling\", \"adult\", \"weapons\"]", "action": "DENY" }, { "ruleType": "ALLOWED_CATEGORIES", "operator": "IN_LIST", "value": "[\"infrastructure\", \"ai_services\", \"marketing\"]", "action": "ALLOW" } ] }' ``` Note: Category matching is **case-insensitive**. If no `category` is provided in the payment request, `ALLOWED_CATEGORIES` rules with `ALLOW` action are skipped (not denied). ## Contract Allowlist Restrict agent interactions to approved smart contracts, protocols, and function selectors. Contract metadata is auto-populated from the **Contract Registry**. ### Setting Up the Contract Registry Register known contracts so the policy evaluator can enrich payment requests with protocol metadata: ```bash curl -X POST https://conto.finance/api/contract-registry \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -d '{ "address": "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", "protocolName": "uniswap", "protocolCategory": "DEX", "label": "Uniswap V2 Router", "chainId": "1" }' ``` ### Contract Allowlist Rules ```json { "policyType": "CONTRACT_ALLOWLIST", "rules": [ { "ruleType": "CONTRACT_ALLOWLIST", "operator": "IN_LIST", "value": "{\"protocols\": [\"uniswap\", \"aave\"], \"categories\": [\"DEX\", \"LENDING\"]}", "action": "ALLOW" } ] } ``` The value supports four filter types (all optional): | Field | Description | Example | | ------------ | --------------------------------- | ------------------------------ | | `contracts` | Allowed contract addresses | `["0x7a25...", "0x1f98..."]` | | `protocols` | Allowed protocol names | `["uniswap", "aave"]` | | `categories` | Allowed protocol categories | `["DEX", "LENDING", "BRIDGE"]` | | `functions` | Allowed 4-byte function selectors | `["0xa9059cbb", "0x095ea7b3"]` | ### Passing Contract Context in Payment Requests Include `targetContractAddress` (and optionally `functionSelector`) in payment requests: ```typescript const result = await conto.payments.request({ amount: 100, recipientAddress: '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', purpose: 'Swap USDC for ETH', targetContractAddress: '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', functionSelector: '0x38ed1739', }); ``` The evaluator automatically looks up `protocolName` and `protocolCategory` from the Contract Registry. If the caller provides them explicitly, those values take precedence over the registry lookup. Note: If a `CONTRACT_ALLOWLIST` rule with `ALLOW` action exists but the payment request does not include `targetContractAddress`, the request is **denied** (fail-closed). This prevents bypassing the allowlist by omitting context. ## Budget Allocations Allocate budgets by department or project: ```json { "policyType": "BUDGET_ALLOCATION", "rules": [ { "type": "DEPARTMENT_BUDGET", "department": "ENGINEERING", "monthlyBudget": 50000, "rollover": false }, { "type": "PROJECT_BUDGET", "projectId": "PROJECT_123", "totalBudget": 100000 } ] } ``` ## Expiration Policies Time-limited permissions: ```json { "policyType": "EXPIRATION", "rules": [ { "type": "VALID_UNTIL", "expiresAt": "2025-12-31T23:59:59Z" }, { "type": "VALID_BETWEEN", "startDate": "2025-07-01", "endDate": "2025-09-30" } ] } ``` Use cases: - Temporary elevated access - Contract-based limits - Trial periods ## Composite Policies Combine multiple conditions with AND/OR logic: ```json { "policyType": "COMPOSITE", "rules": [ { "type": "AND", "conditions": [ { "type": "AMOUNT_LESS_THAN", "amount": 1000 }, { "type": "CATEGORY_IS", "category": "INFRASTRUCTURE" } ] }, { "type": "OR", "conditions": [{ "type": "TRUSTED_COUNTERPARTY" }, { "type": "MANUAL_OVERRIDE" }] } ] } ``` ## Example: High-Security Agent Complete configuration for a high-security agent: ```json [ { "name": "OFAC Compliance", "policyType": "GEOGRAPHIC", "priority": 100, "rules": [{ "type": "BLOCK_COUNTRIES", "countries": ["NK", "IR", "SY", "CU", "RU"] }] }, { "name": "Whitelist Only", "policyType": "WHITELIST", "priority": 90, "rules": [{ "type": "ADDRESS_WHITELIST", "addresses": ["0x..."] }] }, { "name": "Strict Limits", "policyType": "SPEND_LIMIT", "priority": 80, "rules": [ { "type": "PER_TRANSACTION", "maxAmount": 100 }, { "type": "DAILY", "maxAmount": 500 } ] }, { "name": "All Require Approval", "policyType": "APPROVAL_THRESHOLD", "priority": 70, "rules": [{ "type": "AMOUNT_THRESHOLD", "threshold": 0 }] } ] ``` ### x402 Protocol Rules These rules govern HTTP 402 micropayments made through the x402 protocol: | Rule Type | Description | Value Format | | ---------------------------- | ---------------------------------- | ---------------------------------------------------- | | `X402_MAX_PER_REQUEST` | Maximum amount per x402 request | `{"maxAmount": N}` | | `X402_PRICE_CEILING` | Price ceiling for any x402 payment | `{"maxPrice": N}` | | `X402_MAX_PER_ENDPOINT` | Limit per API endpoint | `{"maxAmount": N, "maxCalls": N, "period": "DAILY"}` | | `X402_MAX_PER_SERVICE` | Limit per service domain | `{"maxAmount": N, "maxCalls": N, "period": "DAILY"}` | | `X402_ALLOWED_SERVICES` | Allowlist of service domains | `["api.example.com", "data.service.io"]` | | `X402_BLOCKED_SERVICES` | Blocklist of service domains | `["malicious.site"]` | | `X402_ALLOWED_FACILITATORS` | Allowed x402 facilitator addresses | `["0x..."]` | | `X402_VELOCITY_PER_ENDPOINT` | Rate limit per endpoint | `{"maxCalls": N, "period": "HOUR"}` | | `X402_SESSION_BUDGET` | Session-level spending budget | `{"maxAmount": N}` | ### MPP Protocol Rules These rules govern Machine Payment Protocol (MPP) session-based micropayments: | Rule Type | Description | Value Format | | ----------------------------- | ---------------------------------- | ---------------------------------------------------- | | `MPP_MAX_PER_REQUEST` | Maximum per MPP credential charge | `{"maxAmount": N}` | | `MPP_PRICE_CEILING` | Price ceiling for MPP payments | `{"maxPrice": N}` | | `MPP_MAX_PER_ENDPOINT` | Limit per MPP resource endpoint | `{"maxAmount": N, "maxCalls": N}` | | `MPP_MAX_PER_SERVICE` | Limit per MPP service domain | `{"maxAmount": N, "maxCalls": N, "period": "DAILY"}` | | `MPP_ALLOWED_SERVICES` | Allowlist of MPP service domains | `["api.example.com"]` | | `MPP_BLOCKED_SERVICES` | Blocklist of MPP service domains | `["blocked.site"]` | | `MPP_SESSION_BUDGET` | Maximum deposit per MPP session | `{"maxDeposit": N}` | | `MPP_MAX_SESSION_DEPOSIT` | Hard cap on session deposit amount | `{"maxDeposit": N}` | | `MPP_MAX_CONCURRENT_SESSIONS` | Max active sessions per agent | `{"maxSessions": N}` | | `MPP_MAX_SESSION_DURATION` | Maximum session duration | `{"maxDurationMs": N}` | | `MPP_BLOCK_SESSION_INTENT` | Block specific session intents | `["streaming"]` | | `MPP_ALLOWED_METHODS` | Allowed HTTP methods for MPP | `["GET", "POST"]` | | `MPP_VELOCITY_PER_ENDPOINT` | Rate limit per MPP endpoint | `{"maxCalls": N, "period": "HOUR"}` | ### Card Payment Rules These rules govern card-based payments (Lithic virtual/physical cards): | Rule Type | Description | Value Format | | ------------------------ | ------------------------------- | ---------------------- | | `CARD_MAX_AMOUNT` | Maximum per card transaction | `"500"` | | `CARD_ALLOWED_MCCS` | Allowed merchant category codes | `["5411", "5812"]` | | `CARD_BLOCKED_MCCS` | Blocked merchant category codes | `["7995"]` | | `CARD_ALLOWED_MERCHANTS` | Allowed merchant names | `["Amazon", "Stripe"]` | | `CARD_BLOCKED_MERCHANTS` | Blocked merchant names | `["Casino.com"]` | ## Bulk Policy Operations ### Create Multiple Policies ```bash curl -X POST https://conto.finance/api/policies/bulk \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{ "policies": [ { "name": "Policy 1", "policyType": "SPEND_LIMIT", ... }, { "name": "Policy 2", "policyType": "TIME_WINDOW", ... } ] }' ``` ### Assign to Multiple Agents ```bash curl -X PUT https://conto.finance/api/policies/bulk \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{ "assignments": [ { "policyId": "policy_1", "agentIds": ["agent_a", "agent_b"] }, { "policyId": "policy_2", "agentIds": ["agent_c"] } ] }' ``` --- # Counterparty Policies Counterparty policies control which recipients agents can pay based on trust levels, verification status, and relationships. ## Configuration (API) Create counterparty rules via the Policy Rules API: ```bash curl -X POST https://conto.finance/api/policies/{policyId}/rules \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{ "rules": [ { "ruleType": "ALLOWED_COUNTERPARTIES", "operator": "IN_LIST", "value": "[\"0x1234567890abcdef1234567890abcdef12345678\", \"0xabcdef1234567890abcdef1234567890abcdef12\"]", "action": "ALLOW" }, { "ruleType": "TRUST_SCORE", "operator": "GREATER_THAN_OR_EQUAL", "value": "70", "action": "ALLOW" } ] }' ``` ## Rule Types ### ALLOWED_COUNTERPARTIES Only allow payments to specific wallet addresses (allowlist): ```json { "ruleType": "ALLOWED_COUNTERPARTIES", "operator": "IN_LIST", "value": "[\"0x1234...\", \"0x5678...\"]", "action": "ALLOW" } ``` Address matching is **case-insensitive**. ### BLOCKED_COUNTERPARTIES Block payments to specific addresses: ```json { "ruleType": "BLOCKED_COUNTERPARTIES", "operator": "IN_LIST", "value": "[\"0xdead...\"]", "action": "DENY" } ``` ### TRUST_SCORE Only allow payments to counterparties above a trust threshold: ```json { "ruleType": "TRUST_SCORE", "operator": "GREATER_THAN_OR_EQUAL", "value": "70", "action": "ALLOW" } ``` Trust scores are calculated automatically based on: - Transaction history (30%) - Reliability/success rate (30%) - Account activity (20%) - Verification status (20%) — includes [external providers](https://conto.finance/docs/integrations/trust-providers) like Fairscale (Solana) and sanctions screening Info: The trust score and trust level are automatically fetched during policy evaluation and passed into the rule engine. You don't need to provide them in the payment request — just create the rules and the evaluator populates the context from the counterparty relationship and network trust service. ### COUNTERPARTY_STATUS Require a specific trust level: ```json { "ruleType": "COUNTERPARTY_STATUS", "operator": "EQUALS", "value": "{\"status\": \"TRUSTED\"}", "action": "ALLOW" } ``` ## Trust Levels | Level | Score Range | Description | |-------|-------------|-------------| | `TRUSTED` | 75-100 | High confidence, minimal restrictions | | `VERIFIED` | 50-74 | Established relationship | | `UNKNOWN` | 20-49 | Limited history | | `BLOCKED` | 0-19 | Blocked from transactions | ## Whitelist Policy For maximum control, use `ALLOWED_COUNTERPARTIES` with `ALLOW` action to only allow specific addresses. Any recipient not in the list will be denied: ```json { "ruleType": "ALLOWED_COUNTERPARTIES", "operator": "IN_LIST", "value": "[\"0x1234567890abcdef1234567890abcdef12345678\", \"0xabcdef1234567890abcdef1234567890abcdef12\"]", "action": "ALLOW" } ``` ## Relationship Spend Limits In addition to policy rules, you can set per-counterparty spend limits on the `AgentRelationship` record. These are enforced automatically during payment evaluation: | Field | Description | |-------|-------------| | `spendLimitPerTx` | Maximum amount per transaction to this counterparty | | `spendLimitDaily` | Maximum total spend per day to this counterparty | | `spendLimitMonthly` | Maximum total spend per month to this counterparty | These limits are checked after the trust/block check and before the final approval decision. If a counterparty has a $500/day limit, the evaluator aggregates all confirmed/pending transactions to that address today and blocks if the new payment would exceed it. ```bash # Set per-counterparty limits when creating a relationship curl -X POST https://conto.finance/api/agents/{agentId}/relationships \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{ "counterpartyId": "cp_abc123", "spendLimitPerTx": 200, "spendLimitDaily": 500, "spendLimitMonthly": 5000 }' ``` ## Managing Counterparties ### Add Counterparty ```bash curl -X POST https://conto.finance/api/counterparties \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{ "name": "Amazon Web Services", "type": "VENDOR", "address": "0x1234...", "domain": "aws.amazon.com", "category": "INFRASTRUCTURE", "trustLevel": "TRUSTED" }' ``` ### Block Counterparty ```bash curl -X POST https://conto.finance/api/counterparties/{id}/status \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{ "status": "BLOCKED", "reason": "Suspicious activity detected" }' ``` ## Network Intelligence & External Providers Conto's Network Intelligence provides cross-organization trust signals: - See if other organizations have flagged an address - Benefit from collective fraud detection - Automatic trust score adjustments For Solana wallets, Conto also integrates with [Fairscale](https://fairscale.xyz) for onchain reputation scoring. Wallets with no existing network data are automatically enriched with Fairscale scores, and behavioral red flags generate network alerts. See [Trust & Risk Providers](https://conto.finance/docs/integrations/trust-providers) for configuration and all available providers. Note: Network data is anonymized. Organizations share aggregate signals, not transaction details. ## Best Practices Begin with a small list of trusted vendors: ```json { "policyType": "WHITELIST", "rules": [{ "addresses": ["0xaws...", "0xopenai...", "0xgcp..."] }] } ``` As you verify new vendors, increase their trust level: 1. New vendor: UNKNOWN (auto-blocked) 2. After review: VERIFIED 3. After history: TRUSTED Regularly review trust scores in the dashboard. Investigate any drops. --- # Policy System The policy system is the core of Conto's spending controls. Policies define rules that govern how AI agents can spend funds. ## What is a Policy? A policy is a set of rules that determine whether a payment should be: - **APPROVED** - Payment can proceed - **DENIED** - Payment is blocked - **REQUIRES_APPROVAL** - Manual approval needed ## Policy Types ### Spend Limit Link: https://conto.finance/docs/policies/spend-limits Control maximum amounts per transaction, day, week, or month ### Time Window Link: https://conto.finance/docs/policies/time-windows Restrict transactions to specific hours and days ### Counterparty Link: https://conto.finance/docs/policies/counterparties Control which recipients are allowed based on trust ### Geographic Link: https://conto.finance/docs/policies/advanced#geographic-restrictions-ofac OFAC sanctions screening and country restrictions — built-in and configurable ### Category Allow or block specific spending categories ### Contract Allowlist Link: https://conto.finance/docs/policies/advanced#contract-allowlist Restrict interactions to approved smart contracts and protocols via Contract Registry ### Approval Threshold Require manual approval above certain amounts ### Velocity Limit transaction frequency to prevent rapid drain ### Whitelist Only allow specific pre-approved addresses ### x402 Controls Link: https://conto.finance/docs/policies/advanced#x402-protocol-rules Price ceilings, service allowlists, and budget caps for x402 micropayments ### MPP Controls Link: https://conto.finance/docs/policies/advanced#mpp-protocol-rules Session budgets, concurrency limits, and duration caps for MPP payments ### Budget Allocation Link: https://conto.finance/docs/policies/advanced#budget-allocations Allocate budgets by department or project with period tracking ### Expiration Link: https://conto.finance/docs/policies/advanced#expiration-policies Time-limited permissions with start and end dates ### Card Payment Link: https://conto.finance/docs/policies/advanced#card-payment-rules MCC restrictions, merchant filtering, and amount limits for card payments ## Policy Evaluation Policies are evaluated in a fixed order. When multiple policies are assigned, **all must pass** — the first DENY stops evaluation immediately. ### Evaluation Order 1. **Pre-checks** — Agent must be ACTIVE with linked wallets 2. **Geographic & Sanctions** — OFAC country check + address sanctions screening (always active, no policy needed) 3. **Counterparty Trust** — Pre-fetch trust level and network trust score for use in policy rules 4. **Wallet Policies** — Spend limits, time windows (timezone-aware), and other configured policy rules 5. **Counterparty Rules** — Block list, trust requirements, network intelligence 6. **Relationship Limits** — Per-counterparty spend limits from `AgentRelationship` records 7. **Final Decision** — Aggregate results Info: The default local sanctions provider now reads from Conto's `SanctionedAddress` table instead of a hardcoded demo list. That table is seeded by migration with known high-confidence OFAC matches and refreshed by the `sanctions-list-refresh` background job, so sanctions screening starts with a safe baseline even before the first feed sync completes. | Outcome | Condition | |---------|-----------| | **DENIED** | Any check denies | | **REQUIRES_APPROVAL** | All pass but one requires approval | | **APPROVED** | All checks pass | For a hands-on walkthrough of how evaluation works, see the [Policy Testing guide](https://conto.finance/docs/guides/policy-testing). ## Creating Policies ### Via Dashboard Go to **Policies** in the sidebar and click **New Policy**. | Field | Description | |-------|-------------| | Name | Human-readable name | | Type | Policy type (spend limit, time window, etc.) | | Priority | 0-100 (higher = evaluated first) | | Description | What this policy does | Define the specific rules for this policy. Select which agents this policy applies to. ### Via API ```bash curl -X POST https://conto.finance/api/policies \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Daily Spend Limit", "description": "Limits daily spending to $1000", "policyType": "SPEND_LIMIT", "priority": 50, "isActive": true }' ``` ## Policy Properties | Property | Type | Description | |----------|------|-------------| | `name` | string | Human-readable name | | `description` | string | Detailed description | | `policyType` | enum | Type of policy | | `priority` | number | Evaluation order (0-100) | | `isActive` | boolean | Whether policy is enforced | | `rules` | array | Specific rules for this policy | ## Assigning Policies Policies can be assigned to: - **Agents** - Apply to specific agents - **Wallets** - Apply to specific wallets - **Cards** - Apply to specific payment cards ### Assign to Card ```bash curl -X POST https://conto.finance/api/cards/{cardId}/policies \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -d '{ "policyId": "policy_abc123" }' ``` ### Assign to Agent ```bash curl -X POST https://conto.finance/api/agents/{agentId}/policies \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -d '{ "policyId": "policy_abc123" }' ``` ## Example: Standard Agent Setup A typical agent configuration with multiple policies: ```json [ { "name": "Spend Limits", "policyType": "SPEND_LIMIT", "priority": 50, "rules": [ { "ruleType": "MAX_AMOUNT", "operator": "LTE", "value": "200", "action": "ALLOW" }, { "ruleType": "DAILY_LIMIT", "operator": "LTE", "value": "1000", "action": "ALLOW" }, { "ruleType": "MONTHLY_LIMIT", "operator": "LTE", "value": "10000", "action": "ALLOW" } ] }, { "name": "Business Hours", "policyType": "TIME_WINDOW", "priority": 50, "rules": [ { "ruleType": "TIME_WINDOW", "operator": "BETWEEN", "value": "09:00-18:00", "action": "ALLOW" }, { "ruleType": "DAY_OF_WEEK", "operator": "IN", "value": "MON,TUE,WED,THU,FRI", "action": "ALLOW" } ] }, { "name": "Trusted Vendors Only", "policyType": "COUNTERPARTY", "priority": 50, "rules": [ { "ruleType": "TRUST_SCORE", "operator": "GTE", "value": "60", "action": "ALLOW" } ] } ] ``` ## Best Practices Create policies at different priority levels: - **HIGH (90-100)**: Security/Compliance (sanctions, blocked addresses) - **MEDIUM (40-60)**: Business Rules (limits, time windows) - **LOW (0-20)**: Defaults (catch-all rules) Begin with strict policies and relax based on operational needs. Recommended starting limits for new agents: $100/tx, $500/day, $5,000/month. - Day 1: $100/day limit, 3 trusted vendors - Week 2: $500/day, add 5 more vendors - Month 2: $1,000/day, category-based restrictions Note: Auto-created wallet defaults are higher (daily: $1,000, weekly: $5,000, monthly: $20,000). Override these with policy-level limits for tighter control. Don't block high-value transactions entirely - require approval: ```json { "policyType": "APPROVAL_THRESHOLD", "rules": [ { "ruleType": "REQUIRE_APPROVAL_ABOVE", "operator": "GREATER_THAN", "value": "500", "action": "REQUIRE_APPROVAL" } ] } ``` Use descriptions to explain policy intent: ```json { "name": "OFAC Compliance", "description": "Blocks transactions to OFAC-sanctioned countries. Required for regulatory compliance. Do not modify without legal approval." } ``` ## Available Rule Types Each policy type supports specific rule types. Here is the complete set: | Rule Type | Description | |-----------|-------------| | `MAX_AMOUNT` | Per-transaction limit | | `DAILY_LIMIT` | Daily cumulative spend | | `WEEKLY_LIMIT` | Weekly cumulative spend | | `MONTHLY_LIMIT` | Monthly cumulative spend | | `BUDGET_CAP` | Budget allocation with period | | Rule Type | Description | |-----------|-------------| | `TIME_WINDOW` | Allowed hours (start/end) | | `DAY_OF_WEEK` | Allowed days | | `DATE_RANGE` | Valid date range | | `BLACKOUT_PERIOD` | Block during maintenance/holidays | | `MAINTENANCE_WINDOW` | Alias for blackout period | | `BLOCKED_TIME_WINDOW` | Alias for blackout period | | Rule Type | Description | |-----------|-------------| | `ALLOWED_COUNTERPARTIES` | Whitelist recipient addresses | | `BLOCKED_COUNTERPARTIES` | Blacklist recipient addresses | | `TRUST_SCORE` | Minimum trust score threshold | | `COUNTERPARTY_STATUS` | Required trust level | | Rule Type | Description | |-----------|-------------| | `ALLOWED_CATEGORIES` | Whitelist spending categories | | `BLOCKED_CATEGORIES` | Blacklist spending categories | | `CONTRACT_ALLOWLIST` | Whitelist smart contracts, protocols, categories, and function selectors | | `ALLOWED_CONTRACTS` | Alias for contract allowlist | | `PROTOCOL_ALLOWLIST` | Alias for contract allowlist | Contract metadata (protocol name and category) is auto-populated from your org's **Contract Registry**. Register known contracts via `POST /api/contract-registry` with `address`, `protocolName`, and `protocolCategory`. When an agent includes `targetContractAddress` in a payment request, the evaluator enriches it automatically. | Rule Type | Description | |-----------|-------------| | `GEOGRAPHIC_RESTRICTION` | Country restrictions (OFAC) | | `VELOCITY_LIMIT` | Transaction frequency limits | | `REQUIRE_APPROVAL_ABOVE` | Approval threshold for high-value transactions | | `FAIRSCALE_MIN_SCORE` | Minimum Fairscale reputation score (Solana) | Note: OFAC sanctions screening and address sanctions checks run automatically — no policy rule needed. `GEOGRAPHIC_RESTRICTION` rules add configurable country allow/block lists on top of the built-in checks. | Rule Type | Description | |-----------|-------------| | `CARD_ALLOWED_MCCS` | Whitelist merchant category codes | | `CARD_BLOCKED_MCCS` | Blocklist merchant category codes | | `CARD_ALLOWED_MERCHANTS` | Whitelist merchant names | | `CARD_BLOCKED_MERCHANTS` | Blocklist merchant names | | `CARD_MAX_AMOUNT` | Maximum card payment amount | | Rule Type | Description | |-----------|-------------| | `X402_PRICE_CEILING` | Max amount per API call | | `X402_MAX_PER_ENDPOINT` | Max spend per endpoint per period | | `X402_MAX_PER_SERVICE` | Max spend per service domain per period | | `X402_ALLOWED_SERVICES` | Whitelist service domains | | `X402_BLOCKED_SERVICES` | Blocklist service domains | | `X402_ALLOWED_FACILITATORS` | Whitelist facilitator addresses | | `X402_VELOCITY_PER_ENDPOINT` | Rate limit per endpoint | | `X402_SESSION_BUDGET` | Max spend per session | | Rule Type | Description | |-----------|-------------| | `MPP_PRICE_CEILING` | Max amount per MPP call | | `MPP_MAX_PER_ENDPOINT` | Max spend per endpoint per period | | `MPP_MAX_PER_SERVICE` | Max spend per service domain per period | | `MPP_ALLOWED_SERVICES` | Whitelist service domains | | `MPP_BLOCKED_SERVICES` | Blocklist service domains | | `MPP_VELOCITY_PER_ENDPOINT` | Rate limit per endpoint | | `MPP_SESSION_BUDGET` | Max spend per session | | `MPP_MAX_SESSION_DEPOSIT` | Max deposit per session | | `MPP_MAX_CONCURRENT_SESSIONS` | Max concurrent sessions | | `MPP_MAX_SESSION_DURATION` | Max session duration (hours) | | `MPP_BLOCK_SESSION_INTENT` | Block session intent (one-time only) | | `MPP_ALLOWED_METHODS` | Whitelist payment methods | ## Violation Details When a payment is denied, detailed violation info is returned: ```json { "status": "DENIED", "reasons": ["Would exceed daily limit"], "violations": [ { "type": "DAILY_LIMIT", "limit": 1000, "current": 1150, "message": "[Wallet limit] Would exceed daily limit: $-150.00 remaining of $1000 daily limit", "source": "wallet_limit" }, { "type": "PER_TX_LIMIT", "limit": 200, "current": 300, "message": "[Policy: High-Value Guard] Amount 300 exceeds per-transaction limit of 200", "source": "policy_rule", "policyName": "High-Value Guard" } ] } ``` Each violation includes a `source` field indicating whether the denial came from a **wallet-level limit** (`"wallet_limit"`) or a **policy rule** (`"policy_rule"`). Policy rule violations also include `policyName`. ## Engine Architecture Each rule type is evaluated by a dedicated strategy module. The engine looks up the strategy for a rule's `ruleType` from a registry and delegates evaluation. New rule types can be added without modifying the core engine — register a new strategy and update the SKILL.md docs. ## Next Steps ### Spend Limits Link: https://conto.finance/docs/policies/spend-limits Configure amount-based limits ### Time Windows Link: https://conto.finance/docs/policies/time-windows Set up time-based restrictions --- # Spend Limit Policies Spend limit policies control the maximum amount that can be spent per transaction, day, week, or month. ## Configuration Create a SPEND_LIMIT policy and add rules via the API: ```bash # Create policy curl -X POST https://conto.finance/api/policies \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{"name": "Spend Limits", "policyType": "SPEND_LIMIT", "priority": 50}' # Add rules curl -X POST https://conto.finance/api/policies/{policyId}/rules \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{ "rules": [ {"ruleType": "MAX_AMOUNT", "operator": "LESS_THAN", "value": "500", "action": "ALLOW"}, {"ruleType": "DAILY_LIMIT", "operator": "LESS_THAN", "value": "2000", "action": "ALLOW"}, {"ruleType": "WEEKLY_LIMIT", "operator": "LESS_THAN", "value": "10000", "action": "ALLOW"}, {"ruleType": "MONTHLY_LIMIT", "operator": "LESS_THAN", "value": "30000", "action": "ALLOW"} ] }' ``` ## Rule Types | Rule Type | Description | Operator | Value | |-----------|-------------|----------|-------| | `MAX_AMOUNT` | Maximum per single transaction | `LESS_THAN` | Amount (number) | | `DAILY_LIMIT` | Maximum total spend per day | `LESS_THAN` | Amount (number) | | `WEEKLY_LIMIT` | Maximum total spend per week | `LESS_THAN` | Amount (number) | | `MONTHLY_LIMIT` | Maximum total spend per month | `LESS_THAN` | Amount (number) | | `BUDGET_CAP` | Budget with period | `LESS_THAN` | `{"amount": N, "period": "MONTHLY"}` | ## How It Works 1. **Per-Transaction**: Checked against the requested amount 2. **Daily/Weekly/Monthly**: Checked against cumulative spend + requested amount Info: Conto uses fixed-precision monetary values for spend limits, balances, and transaction amounts to avoid floating-point rounding issues in financial calculations. ### Example Evaluation ``` Agent: Operations Agent Daily Limit: $1,000 Spent Today: $750 Request: $300 payment Evaluation: - Per-tx check: $300 < limit (pass) - Daily check: $750 + $300 = $1,050 > $1,000 (FAIL) Result: DENIED Reason: "Would exceed daily limit: $250 remaining" ``` ## Wallet-Level Limits In addition to policies, limits can be set on the agent-wallet link: ```json { "agentId": "agent_abc", "walletId": "wallet_xyz", "spendLimitPerTx": 100, "spendLimitDaily": 1000, "spendLimitWeekly": 5000, "spendLimitMonthly": 15000 } ``` These are evaluated **first**, before policy rules. Info: **Auto-creation defaults:** When an external wallet is auto-created, the system applies these defaults: daily = $1,000, weekly = $5,000, monthly = $20,000, no per-transaction limit. You can override these when linking a wallet to an agent. Setting `spendLimitPerTx` to `0` blocks all payments at the wallet level before policies are evaluated. ## Tracking Spend The system tracks spending automatically: - **spentToday** - Resets at midnight UTC - **spentThisWeek** - Resets Monday midnight UTC - **spentThisMonth** - Resets 1st of month midnight UTC ## Best Practices Begin with low limits and increase based on operational needs: ```json { "type": "PER_TRANSACTION", "maxAmount": 50 // Start low } ``` Use multiple limit types for defense in depth: - Per-tx: Prevents single large payments - Daily: Limits daily exposure - Monthly: Controls overall budget Create different policies for different agent risk levels: - Low-risk agents: Higher limits - New agents: Lower limits until proven - Critical agents: Strict limits + approval ## API Example ```bash # Create spend limit policy curl -X POST https://conto.finance/api/policies \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Standard Spend Limits", "policyType": "SPEND_LIMIT", "priority": 50, "isActive": true }' # Add rules curl -X POST https://conto.finance/api/policies/{policyId}/rules \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{ "rules": [ { "ruleType": "MAX_AMOUNT", "operator": "LESS_THAN", "value": "500", "action": "ALLOW" }, { "ruleType": "DAILY_LIMIT", "operator": "LESS_THAN", "value": "2000", "action": "ALLOW" } ] }' ``` --- # Time Window Policies Time window policies restrict when transactions can occur based on hours and days. ## Configuration (API) Create rules via the Policy Rules API: ```bash curl -X POST https://conto.finance/api/policies/{policyId}/rules \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{ "rules": [ { "ruleType": "TIME_WINDOW", "operator": "BETWEEN", "value": "{\"start\": \"09:00\", \"end\": \"17:00\"}", "action": "ALLOW" }, { "ruleType": "DAY_OF_WEEK", "operator": "IN_LIST", "value": "[\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\"]", "action": "ALLOW" } ] }' ``` ## Rule Types ### TIME_WINDOW (Hours) Restrict transactions to specific hours of the day: | Property | Description | |----------|-------------| | `ruleType` | `TIME_WINDOW` | | `operator` | `BETWEEN` (allow within window) or `NOT_BETWEEN` (block within window) | | `value` | JSON: `{"start": "HH:MM", "end": "HH:MM"}` | | `action` | `ALLOW` or `DENY` | ### DAY_OF_WEEK (Days) Restrict transactions to specific days of the week: | Property | Description | |----------|-------------| | `ruleType` | `DAY_OF_WEEK` | | `operator` | `IN_LIST` (allow these days) or `NOT_IN_LIST` (block these days) | | `value` | JSON array: `["Mon", "Tue", "Wed", "Thu", "Fri"]` | | `action` | `ALLOW` or `DENY` | Valid day values: `Mon`, `Tue`, `Wed`, `Thu`, `Fri`, `Sat`, `Sun` ### BLACKOUT_PERIOD Block transactions during maintenance windows or holidays: ```bash curl -X POST https://conto.finance/api/policies/{policyId}/rules \ -H "Authorization: Bearer $CONTO_API_KEY" \ -d '{ "rules": [{ "ruleType": "BLACKOUT_PERIOD", "operator": "BETWEEN", "value": "{\"windows\": [{\"start\": \"02:00\", \"end\": \"06:00\", \"reason\": \"Maintenance\", \"recurring\": true}]}", "action": "DENY" }] }' ``` ## Wallet-Level Time Windows Time windows can also be set on the agent-wallet link. These include a `timezone` field that controls how hours and days are evaluated: ```json { "agentId": "agent_abc", "walletId": "wallet_xyz", "allowedHoursStart": 9, "allowedHoursEnd": 17, "allowedDays": ["Mon", "Tue", "Wed", "Thu", "Fri"], "timezone": "America/New_York" } ``` Info: All wallet-level time checks are evaluated in the wallet's configured timezone (defaults to `UTC`). This means a "9-17" window with `timezone: "America/New_York"` will correctly evaluate against Eastern Time, regardless of where the server is deployed. Uses IANA timezone names (e.g., `America/Los_Angeles`, `Europe/London`, `Asia/Tokyo`). ## Use Cases ### Business Hours Only allow transactions during working hours ```json { "startHour": 9, "endHour": 18, "allowedDays": ["Mon", "Tue", "Wed", "Thu", "Fri"] } ``` ### Extended Hours Allow transactions in extended support hours ```json { "startHour": 7, "endHour": 22, "allowedDays": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] } ``` ### Weekends Only For agents that operate on weekends ```json { "startHour": 0, "endHour": 24, "allowedDays": ["Sat", "Sun"] } ``` ### 24/7 No time restrictions (allow always) ```json { "startHour": 0, "endHour": 24, "allowedDays": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] } ``` ## Error Response When a transaction is blocked by time window: ```json { "status": "DENIED", "reasons": ["Transaction outside allowed hours"], "violations": [ { "type": "TIME_WINDOW", "limit": 0, "current": 0, "message": "Transactions not allowed at 22:00 (America/New_York). Allowed hours: 9:00 - 17:00", "source": "wallet_limit" } ] } ``` ## Best Practices Set the `timezone` field on the agent-wallet link to match the organization's operating timezone. Time window violations will include the timezone in the error message for clarity. ```json { "allowedHoursStart": 9, "allowedHoursEnd": 17, "timezone": "America/New_York" } ``` If unset, defaults to `UTC`. All IANA timezone names are supported. Align time windows with when humans are available to monitor: - During work hours: Standard limits - After hours: Stricter limits or blocked Allow small transactions 24/7, but require approval after hours: ```json [ { "policyType": "TIME_WINDOW", "priority": 50, "rules": [{ "type": "HOURS", "startHour": 9, "endHour": 17 }] }, { "policyType": "APPROVAL_THRESHOLD", "priority": 40, "rules": [{ "type": "AFTER_HOURS_THRESHOLD", "threshold": 50 }] } ] ``` --- # Notification Channels Notification channels send approval requests to external platforms. Approvers can approve or reject directly from their email inbox, Slack workspace, Telegram chat, or WhatsApp conversation. ## How It Works ``` Payment triggers approval workflow → Conto generates secure, one-time action tokens → Notifications sent to all configured channels → Approver clicks Approve/Reject → Token is validated and consumed → Same approval logic as the dashboard ``` Every external decision uses the same `submitApprovalDecision()` path. Audit logs, atomic counters, webhook delivery, and race-condition safety all apply regardless of channel. ## Supported Channels | Channel | Delivery | How Approvers Act | | -------- | ------------------------------ | --------------------------------- | | Email | Rich HTML via Resend | Click Approve/Reject button links | | Slack | Block Kit message with buttons | Click interactive action buttons | | Telegram | Message with inline keyboard | Tap inline keyboard buttons | | WhatsApp | Interactive button message | Tap reply buttons | | Webhook | JSON POST with tokens | POST tokens back to Conto API | ## Setting Up Channels Go to **Settings** > **Channels** in the Conto dashboard, or use the REST API. Click **Add Channel** and select the platform: Email, Slack, Telegram, WhatsApp, or Webhook. Each channel type requires different configuration: | Channel | Required Config | |---------|----------------| | Email | Comma-separated recipient email addresses | | Slack | Bot token (`xoxb-...`) and channel ID | | Telegram | Bot token and chat ID | | WhatsApp | Phone number ID, access token, recipient numbers | | Webhook | HTTPS URL to receive JSON payloads | Webhook targets must resolve to public addresses. Conto rejects loopback, private, link-local, and other internal-only destinations. In production, webhook channels must use HTTPS. Choose which events trigger notifications: | Event | When It Fires | |-------|--------------| | `approval.requested` | New payment needs approval | | `approval.decided` | Someone approved or rejected | | `approval.escalated` | Request escalated after timeout | | `approval.expired` | Request expired without resolution | Click the test button to send a sample notification and verify your configuration. ## Channel Configuration Details Uses Resend to deliver rich HTML emails. Each eligible approver receives their own email with unique Approve and Reject buttons. **Config fields:** - `recipients` - List of email addresses. Only addresses matching eligible approvers receive actionable emails. Clicking a button opens a browser, validates the token, submits the decision, and shows a confirmation page. Requires a Slack app with the `chat:write` bot scope. Messages use Block Kit with payment details and interactive Approve/Reject buttons. **Config fields:** - `botToken` - Your Slack app's bot token (`xoxb-...`) - `channelId` - The Slack channel ID to post messages to **Setup:** 1. Create a Slack app at [api.slack.com/apps](https://api.slack.com/apps) 2. Add the `chat:write` bot scope 3. Install the app to your workspace 4. Set the interactivity request URL to `https://conto.finance/api/webhooks/slack` 5. Set `SLACK_SIGNING_SECRET` in your Conto environment When an approver clicks a button, Slack sends the interaction to Conto. The message is updated to show the result. Uses the Telegram Bot API to send messages with inline keyboard buttons. **Config fields:** - `botToken` - Your Telegram bot token from [@BotFather](https://t.me/BotFather) - `chatId` - The chat or group ID to send messages to **Setup:** 1. Create a bot via @BotFather 2. Set the webhook URL: `https://api.telegram.org/bot{token}/setWebhook?url=https://conto.finance/api/webhooks/telegram` 3. Add the bot to your group chat When an approver taps a button, the message updates to show the result. Uses the WhatsApp Cloud API to send interactive button messages. **Config fields:** - `phoneNumberId` - Your WhatsApp Business phone number ID - `accessToken` - Permanent access token from Meta - `recipientNumbers` - Phone numbers in international format (e.g., `+1234567890`) **Setup:** 1. Register at [developers.facebook.com](https://developers.facebook.com) 2. Create a WhatsApp Business app 3. Set the webhook URL to `https://conto.finance/api/webhooks/whatsapp` 4. Set `WHATSAPP_APP_SECRET` and `WHATSAPP_VERIFY_TOKEN` in your Conto environment 5. Subscribe to the `messages` webhook field Sends a signed JSON payload to any HTTPS endpoint. Use this for custom integrations that need to review and submit approval decisions. **Config fields:** - `url` - Your HTTPS endpoint URL **Validation rules:** - The destination must resolve to a public IP address - Private, loopback, link-local, and `.internal` / `.local` hosts are rejected - In production, only `https://` webhook targets are accepted **Payload format:** ```json { "event": "approval.requested", "approvalRequestId": "clxyz123...", "paymentDetails": { "amount": 500, "currency": "USDC", "recipientAddress": "0x5678...", "agentName": "Treasury Agent" }, "tokens": { "approve": "base64url-token", "reject": "base64url-token" }, "actionUrl": "https://conto.finance/api/approvals/action", "timestamp": "2026-04-06T12:00:00Z" } ``` To submit a decision, POST the token back to the `actionUrl`: ```bash curl -X POST https://conto.finance/api/approvals/action \ -H "X-Action-Token: base64url-token" \ -H "Content-Type: application/json" \ -d '{"comment": "Approved via external review system"}' ``` ## REST API Manage channels programmatically: ```bash # List channels curl https://conto.finance/api/notification-channels \ -H "Authorization: Bearer $API_KEY" # Create a channel curl -X POST https://conto.finance/api/notification-channels \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "channelType": "SLACK", "name": "Treasury Channel", "config": { "botToken": "xoxb-...", "channelId": "C012345" }, "eventTypes": ["approval.requested", "approval.decided"] }' # Test a channel curl -X POST https://conto.finance/api/notification-channels/{id}/test \ -H "Authorization: Bearer $API_KEY" # Delete a channel curl -X DELETE https://conto.finance/api/notification-channels/{id} \ -H "Authorization: Bearer $API_KEY" ``` ## Action Token Security Action tokens are the core security mechanism for external approvals. - **32 bytes of cryptographic randomness**, base64url-encoded - **Protected at rest**. Plaintext tokens are not retained after issuance - **One-time use**. Tokens cannot be reused after a decision is recorded - **Time-limited**. Tokens expire when the approval request expires (default 24 hours) - **Per-user, per-action**. Each approver gets separate Approve and Reject tokens - **Full audit trail**. Every decision records the channel, token ID, IP address, and user agent ## Environment Variables | Variable | Required For | Description | | ----------------------- | ------------ | -------------------------------------------------- | | `RESEND_API_KEY` | Email | Resend API key for sending emails | | `SLACK_SIGNING_SECRET` | Slack | Slack app signing secret for webhook verification | | `WHATSAPP_APP_SECRET` | WhatsApp | Meta app secret for webhook signature verification | | `WHATSAPP_VERIFY_TOKEN` | WhatsApp | Token for Meta webhook verification challenge | ## Webhook Payload Validation Inbound webhook payloads from Slack, Telegram, and WhatsApp are validated against typed Zod schemas before processing. Malformed payloads are rejected with a `400` response and never reach handler logic. Signature verification (HMAC) still runs first for all three providers regardless of payload shape. ## Next Steps ### Approval Workflows Link: https://conto.finance/docs/policies/advanced Configure multi-approval workflows with escalation and sequential approvals ### Securing Agents Link: https://conto.finance/docs/guides/securing-agents Set up spending limits and approval thresholds for your agents --- # Trust & Risk Providers Conto integrates with external providers to enrich trust scores, screen for sanctions, and assess wallet reputation. These providers feed into the [trust score calculation](https://conto.finance/docs/introduction/concepts#trust-score-factors) (via the **Verification** factor, weighted at 20%) and the [policy rule engine](https://conto.finance/docs/policies/advanced#supported-rule-types). Reputation providers (like Fairscale) follow a **fail-open** design: if unavailable, Conto logs a warning and continues without blocking transactions. Sanctions screening providers follow a **fail-closed** design: if Chainalysis or TRM Labs is unavailable, the address is treated as sanctioned to prevent compliance gaps. ## Fairscale (Solana Reputation) [Fairscale](https://fairscale.xyz) provides composable reputation scoring for Solana wallets. It analyzes onchain behavioral signals — token holdings, transaction patterns, staking activity, and social connections — to produce a 0-100 reputation score. ### What it provides | Signal | Description | | ----------------- | ------------------------------------------------------------------------------------------------- | | **Score** (0-100) | Composite reputation score | | **Tier** | `bronze`, `silver`, or `gold` | | **Pillars** | Six scored dimensions: verification, reliability, social, track record, economic stake, ecosystem | | **Badges** | Behavioral badges (e.g. "LST Staker", "No Instant Dumps") | ### How Conto uses it - **Trust score enrichment**: For Solana counterparties with no existing network data, Fairscale scores are normalized (0-100 to 0.0-1.0) and used as the network trust score - **Cold-start enrichment**: Unknown Solana addresses get real trust scores instead of blank `UNKNOWN` defaults - **Policy rules**: Use `FAIRSCALE_MIN_SCORE` to require a minimum Fairscale score before allowing payments ### Policy rule Block payments to Solana wallets below a Fairscale score threshold: ```json { "ruleType": "FAIRSCALE_MIN_SCORE", "operator": "GREATER_THAN_OR_EQUAL", "value": "50", "action": "ALLOW" } ``` ### Availability Fairscale is **supported when the Conto deployment is configured with a `FAIRSCALE_API_KEY`**. If Fairscale is not configured, Conto skips reputation enrichment and continues without blocking the request. Note: Fairscale is **Solana-only**. It is automatically skipped for EVM addresses, and Conto also skips it when the provider is unavailable. ### SDK API response When querying trust data for a Solana address via the SDK, Fairscale data is included: ```json { "address": "CKs1E69a2e9TmH4mKKLrXFF8kD3ZnwKjoEuXa6sz9WqX", "fairscale": { "score": 8, "tier": "bronze", "confidence": 0.3, "isVerified": true, "pillars": { "verification": 0, "reliability": 8, "social": 14, "track_record": 0, "economic_stake": 41, "ecosystem": 5 }, "badges": [ { "id": "lst_staker", "label": "LST Staker", "tier": "gold" }, { "id": "no_dumper", "label": "No Instant Dumps", "tier": "silver" } ] } } ``` --- ## Sanctions Screening Conto screens wallet addresses against sanctions lists to support compliance requirements. Screening checks known sanctioned addresses (Tornado Cash, Lazarus Group, Garantex, etc.) and can be extended with enterprise providers for deeper risk analysis. ### Providers | Provider | Description | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Local OFAC** (default) | Built-in OFAC SDN list — screens against known sanctioned wallet addresses. No API key needed. | | **Chainalysis** | Enterprise-grade blockchain risk scoring via the [Chainalysis KYT API](https://www.chainalysis.com/). Identifies sanctions exposure through cluster analysis. | | **TRM Labs** | Blockchain intelligence and compliance via the [TRM Labs Screening API](https://www.trmlabs.com/). Screens addresses for sanctions risk indicators. | ### Availability **Local OFAC** screening is **built into Conto** — no configuration needed. All organizations on [conto.finance](https://conto.finance) automatically get OFAC sanctions screening against known sanctioned addresses. For enterprise compliance needs, **Chainalysis** and **TRM Labs** provide deeper risk analysis. Contact [support](https://conto.finance) to enable enterprise sanctions screening for your organization. Note: Sanctions screening is **fail-closed** for enterprise providers: if Chainalysis or TRM Labs is unavailable, the address is treated as sanctioned to prevent compliance gaps. Local OFAC screening is always available since it uses a built-in address list. ### Policy rules Block transactions to sanctioned countries using `GEOGRAPHIC_RESTRICTION`: ```json { "ruleType": "GEOGRAPHIC_RESTRICTION", "operator": "IN_LIST", "value": "[\"CU\", \"IR\", \"KP\", \"SY\", \"RU\"]", "action": "DENY" } ``` See the [OFAC compliance section](https://conto.finance/docs/policies/advanced#geographic-restrictions-ofac) for the full list of sanctioned countries. Always consult legal counsel for compliance requirements. This is not legal advice. --- ## Network Intelligence In addition to external providers, Conto's built-in **Network Intelligence** aggregates anonymized trust signals across all organizations on the platform: - Cross-organization address flagging - Collective fraud detection - Automatic trust score adjustments based on network-wide behavior Note: Network Intelligence data is anonymized. Organizations share aggregate trust signals, not transaction details. --- ## Provider priority When multiple sources have data for an address, Conto applies them in this order: 1. **Conto Network Intelligence** — on-platform transaction history and cross-org signals 2. **Fairscale** (Solana only) — external reputation scoring 3. **Sanctions screening** — compliance blocklists (always enforced regardless of trust score) Network Intelligence scores take precedence because they're based on real transaction history. Fairscale is used for cold-start enrichment when no network data exists. Sanctions screening operates independently and can block transactions regardless of trust score. ## Implementation Trust scoring is split into two complementary services: - **Per-organization trust** (`trust-score.ts`) — your org's local view of a counterparty derived from your own transaction history with them. - **Network trust** (`network-trust-service.ts`) — the cross-organization "credit bureau" view aggregated across all Conto customers. Most policies that reference a `TRUST_SCORE` rule consult the per-org service first and fall back to the network score for unknown addresses. See [Trust Scoring](https://conto.finance/docs/guides/trust-scoring) for the full data model. --- # Approval Workflows for Agent Payments Approval workflows let you keep low-risk agent payments fast while routing exceptional cases through human review. In Conto, approvals are part of the payment control plane, not an afterthought layered on later. ## When to Use Approval Workflows Approval workflows are a strong fit when you need any of the following: - Payments above a finance threshold - New recipients that have not built trust yet - Category-specific review, such as treasury or vendor onboarding flows - Role-based approvals for finance, ops, or compliance - Dual control or sequential review for larger transfers ## How the Flow Works ```mermaid flowchart TD Payment["Agent requests payment"] --> Policy["Policy engine + workflow matching"] Policy --> Match{"Workflow matched?"} Match -->|"No"| Continue["Normal approval / deny path"] Match -->|"Yes"| Create["Create approval request"] Create --> Notify["Send notifications to external channels"] Notify --> Decision["Approver decisions"] Decision --> Approved{"Required approvals reached?"} Decision --> Rejected{"Any rejection?"} Approved -->|"Yes"| Execute["Payment status moves to APPROVED"] Approved -->|"No"| Pending["Stay pending"] Rejected -->|"Yes"| Deny["Payment status moves to DENIED"] Rejected -->|"No"| Pending Execute --> Audit["Audit log + webhook + channel record"] Deny --> Audit ``` ## How Conto Matches a Workflow Conto evaluates active workflows in priority order and picks the first workflow whose trigger conditions match the payment context. ### Supported trigger conditions | Trigger | What it matches | | ------------------------ | ------------------------------------------------------------- | | `amountThreshold` | Amounts at or above a threshold | | `currency` | Currency-specific review | | `categories` | Category-based review, such as vendor or infrastructure spend | | `agentTypes` | Specific agent frameworks or classes | | `newRecipients` | First-time recipients | | `counterpartyTrustLevel` | Low-trust or unknown counterparties | This makes approval workflows a good complement to trust scoring and counterparty rules. For example, you can auto-approve trusted vendors while forcing review for unknown recipients. ## Workflow Settings That Matter | Setting | What it does | | -------------------- | --------------------------------------------------------- | | `priority` | Higher-priority workflows match first | | `requiredApprovals` | Number of approvals needed before a payment moves forward | | `timeoutHours` | Expiration window for pending requests | | `allowSelfApproval` | Whether the initiator can approve their own request | | `approverRoles` | Role-based access, such as `OWNER` or `ADMIN` | | `specificApprovers` | Explicit user allowlist for a workflow | | `sequentialApproval` | Enforces approval order when specific approvers are set | Info: Any rejection ends the workflow immediately. Approvals accumulate until the required approval count is reached. ## External Approval Channels Approval requests can be delivered to external channels so finance or ops teams can act without logging into Conto for every review. Supported channels include: - Slack - Email - Telegram - WhatsApp - Webhook Each decision records the acting channel, and Conto keeps an audit trail of who approved, when they approved, and how the request was resolved. For step-by-step setup, see [/guides/external-approvals](https://conto.finance/docs/guides/external-approvals). ## Recommended Patterns ### Pattern 1: Single approval for large payments | Setting | Example | | ------------------ | --------------------------------------------------------- | | Trigger | `amountThreshold = 100` | | Required approvals | `1` | | Approver roles | `OWNER`, `ADMIN` | | Best for | Day-to-day spend that only needs review above a threshold | ### Pattern 2: Review first-time recipients | Setting | Example | | ------------------ | ------------------------------------------------------------------------ | | Trigger | `newRecipients = true` | | Required approvals | `1` | | Approver roles | `ADMIN` | | Best for | Preventing agents from sending funds to unknown addresses without review | ### Pattern 3: Dual control for sensitive transfers | Setting | Example | | ------------------ | ---------------------------------------------------- | | Trigger | `amountThreshold = 5000` | | Required approvals | `2` | | Self approval | `false` | | Best for | Treasury, vendor onboarding, or high-value transfers | ### Pattern 4: Sequential approval for finance + security | Setting | Example | | ------------------- | ----------------------------------------------------------------- | | Specific approvers | `finance lead`, then `security lead` | | Sequential approval | `true` | | Best for | Controls that require ordered sign-off from multiple stakeholders | ## Pair Approval Workflows with Trust Scoring One of the highest-signal combinations is: 1. Use trust scoring to classify counterparties. 2. Let trusted or verified recipients flow normally. 3. Route unknown or deteriorating counterparties into approval workflows. That gives you a fast path for established counterparties and a controlled path for new or risky ones. ## Canonical Approval Architecture The most common production stack looks like this: - Policy engine blocks clearly disallowed payments outright. - Trust scoring enriches the recipient before the payment is evaluated. - Approval workflows catch the gray area between auto-approve and hard deny. - External channels deliver requests to the real stakeholders. - Audit logs and webhooks make the outcome visible to finance and operations systems. ## Related Guides ### External Approvals Link: https://conto.finance/docs/guides/external-approvals Connect Slack, email, Telegram, WhatsApp, or webhooks ### Trust Scoring Link: https://conto.finance/docs/guides/trust-scoring Use counterparty trust as an approval trigger ### Securing Agents Link: https://conto.finance/docs/guides/securing-agents See where approvals fit in a layered policy model ### Architecture Patterns Link: https://conto.finance/docs/guides/architecture-patterns Visual reference for approval and payment flows --- # Architecture Patterns These are the core integration patterns teams implement with Conto in production. Use them as canonical reference diagrams when you are designing a new agent payment workflow or documenting an existing one. ## 1. Managed Execution with an Integrated Wallet Use this pattern when Conto should both evaluate policy and orchestrate execution through an integrated wallet provider such as Privy or Sponge. ```mermaid flowchart LR Agent["Agent runtime"] --> Request["POST /api/sdk/payments/request\nautoExecute=true"] Request --> Policy["Policy engine"] Policy --> Decision{"Approved?"} Decision -->|"Yes"| Wallet["Integrated wallet provider\nPrivy or Sponge"] Decision -->|"Requires review"| Approval["Approval workflow"] Decision -->|"No"| Denied["Denied with violations"] Approval -->|"Approved"| Wallet Approval -->|"Rejected"| Denied Wallet --> Chain["Blockchain settlement"] Chain --> Audit["Transactions, audit logs,\nalerts, analytics"] ``` - Best for: teams that want fewer moving parts and Conto-managed execution. - Common controls: spend limits, approval thresholds, trust rules, time windows. ## 2. External Wallet Approval + Confirm Use this pattern when your agent already controls its own wallet or transfer tools and Conto acts as the policy gate. ```mermaid flowchart LR Agent["Agent runtime"] --> Approve["POST /api/sdk/payments/approve"] Approve --> Policy["Policy engine"] Policy --> Gate{"Approved?"} Gate -->|"Yes"| Transfer["Agent executes transfer\nwith its own keys or wallet tools"] Gate -->|"Requires review"| Review["Approval workflow"] Gate -->|"No"| Denied["Denied with reasons"] Review -->|"Approved"| Transfer Review -->|"Rejected"| Denied Transfer --> Confirm["POST /api/sdk/payments/{id}/confirm"] Confirm --> Audit["Conto records tx hash,\nstatus, audit trail"] ``` - Best for: OpenClaw and Hermes agents that already have wallet tools. - Canonical flow: `approve -> transfer -> confirm`. ## 3. x402 API Control Loop Use this pattern when an API returns `402 Payment Required` and each paid call is a separate machine payment event. ```mermaid flowchart LR Agent["Agent calls paid API"] --> Challenge["HTTP 402 challenge"] Challenge --> PreAuth["POST /api/sdk/x402/pre-authorize"] PreAuth --> Policy["x402 policy controls"] Policy --> Allowed{"Authorized?"} Allowed -->|"Yes"| Pay["Wallet signs x402 payment"] Allowed -->|"No"| Stop["Abort or escalate"] Pay --> Retry["Retry API call with payment"] Retry --> Record["POST /api/sdk/x402/record"] Record --> Analytics["Budgets, service stats,\nauditability"] ``` - Best for: pay-per-use APIs and machine commerce with explicit per-call pricing. - Core controls: price ceilings, service allowlists, endpoint velocity, service budgets. ## 4. MPP Session Lifecycle Use this pattern when an agent will make repeated requests to the same service and wants a session budget instead of separate onchain settlement for each call. ```mermaid flowchart LR Agent["Agent receives MPP payment challenge"] --> PreAuth["POST /api/sdk/mpp/pre-authorize"] PreAuth --> Policy["MPP policy controls"] Policy --> Session{"Authorized?"} Session -->|"Yes"| Open["Open session with deposit"] Session -->|"No"| Stop["Abort or escalate"] Open --> Charges["Multiple service requests\nconsume session budget"] Charges --> Close["Close session and settle"] Close --> Record["POST /api/sdk/mpp/record"] Record --> Analytics["Session budgets,\nservice usage, burn rate"] ``` - Best for: chat, streaming, iterative processing, or high-frequency service calls. - Core controls: session budget, max concurrent sessions, max duration, allowed methods. ## 5. Human Approval Workflow Approval workflows sit in front of execution and resolve high-risk or policy-triggered payments before money moves. ```mermaid flowchart TD Request["Payment request"] --> Match["Find highest-priority matching workflow"] Match --> Create["Create approval request"] Create --> Notify["Dispatch to Slack, email,\nTelegram, WhatsApp, or webhook"] Notify --> Decide["Approver decision(s)"] Decide --> Enough{"Enough approvals?"} Decide --> Reject{"Any rejection?"} Enough -->|"Yes"| Approved["Payment approved"] Enough -->|"No"| Pending["Still pending"] Reject -->|"Yes"| Denied["Payment denied"] Reject -->|"No"| Pending Approved --> Audit["Record channel, actor,\nstatus, audit trail"] Denied --> Audit ``` - Best for: large payments, new recipients, low-trust counterparties, and finance review. - Supports multi-approver, role-based, specific-approver, and sequential workflows. ## 6. Trust Scoring Feedback Loop Trust scoring is not just a lookup; it is a feedback loop that continuously changes how counterparties are evaluated. ```mermaid flowchart LR Counterparty["Counterparty address"] --> History["Transaction history"] Counterparty --> Reliability["Failures, flags, alerts"] Counterparty --> Activity["Age, recency, consistency"] Counterparty --> Verification["Verification + network data"] Verification --> Fairscale["Fairscale for Solana\ncold-start enrichment"] Verification --> Network["Conto network intelligence"] History --> Score["Trust score + trust level"] Reliability --> Score Activity --> Score Fairscale --> Score Network --> Score Score --> Policies["Counterparty and approval policies"] Policies --> Outcomes["Allow, require approval,\nor deny"] ``` - Best for: vendor controls, trust-based approvals, network-aware risk gating. - Related docs: [/guides/trust-scoring](https://conto.finance/docs/guides/trust-scoring), [/integrations/trust-providers](https://conto.finance/docs/integrations/trust-providers) ## Which Pattern Should You Start With? | Goal | Recommended pattern | | ----------------------------------------------- | ---------------------------------------- | | Fastest first payment with minimal custom logic | Managed execution with integrated wallet | | Add guardrails to an existing agent wallet | External wallet approval + confirm | | Govern machine-paid APIs | x402 API control loop | | Govern repeated session spend | MPP session lifecycle | | Add finance or security review | Human approval workflow | | Route based on recipient quality and risk | Trust scoring feedback loop | ## Related Guides ### Choose Your Integration Link: https://conto.finance/docs/guides/choose-your-integration Decide between SDK, OpenClaw, Hermes, x402, and MPP ### Approval Workflows Link: https://conto.finance/docs/guides/approval-workflows Configure review and escalation logic ### Trust Scoring Link: https://conto.finance/docs/guides/trust-scoring Understand how recipient trust feeds policy decisions ### Recipes Link: https://conto.finance/docs/guides/recipes Copy-paste commands for the main flows above --- # Audit Logs Every action in Conto — creating agents, executing payments, changing policies, freezing accounts — is recorded in an immutable audit log with SHA-256 hash chains for tamper detection. ## What Gets Logged ### Actions | Action | Description | |--------|-------------| | `CREATE` | Resource created (agent, wallet, policy, etc.) | | `UPDATE` | Resource modified | | `DELETE` | Resource removed | | `APPROVE` / `REJECT` | Approval decision made | | `SUSPEND` / `ACTIVATE` | Agent frozen or unfrozen | | `FUND` / `WITHDRAW` / `TRANSFER` | Wallet balance changes | | `PAYMENT_EXECUTED` | Payment submitted onchain | | `PAYMENT_CONFIRMED` | Payment confirmed onchain | | `PAYMENT_DENIED` | Payment blocked by policy | | `PAYMENT_FAILED` | Payment transaction failed | | `EXTERNAL_PAYMENT_APPROVAL` | External wallet payment approved | | `EXTERNAL_PAYMENT_CONFIRMED` | External wallet payment confirmed | | `POLICY_VIOLATION` | Policy rule triggered | | `ALERT_CREATED` / `ALERT_RESOLVED` | Alert lifecycle | | `SETTINGS_CHANGED` | Organization settings modified | | `PERMISSION_GRANTED` / `PERMISSION_REVOKED` | Role or scope changes | | `LOGIN` / `LOGOUT` | User session events | | `AUDIT_LOG_EXPORTED` | Audit log data exported | ### Resources Logs track actions on: `agent`, `wallet`, `transaction`, `policy`, `policy_rule`, `organization`, `user`, `member`, `api_key`, `sdk_key`, `alert`, `counterparty`, `relationship`, `settings`, `approval_request`, `approval_workflow`, `payment_request`. ## Log Entry Fields Each audit log entry contains: | Field | Description | |-------|-------------| | `action` | What happened (CREATE, UPDATE, DELETE, etc.) | | `resource` | What type of resource was affected | | `resourceId` | ID of the affected resource | | `previousState` | State before the change (JSON) | | `newState` | State after the change (JSON) | | `actorType` | Who did it: USER, API, AGENT, or SYSTEM | | `actorId` | User ID, API key ID, or "system" | | `ipAddress` | Client IP (from reverse proxy headers) | | `userAgent` | Client user agent string | | `metadata` | Additional context (audit hash, change diff, API key ID) | | `createdAt` | When the action occurred | ## Change Detection When a resource is updated, the audit service automatically diffs the previous and new states and records which fields changed: ```json { "changes": [ { "field": "status", "oldValue": "ACTIVE", "newValue": "SUSPENDED" }, { "field": "spendLimitDaily", "oldValue": 1000, "newValue": 500 } ] } ``` ## Tamper-Evident Hash Chain Each log entry includes a SHA-256 hash computed over the previous entry's hash, the timestamp, action, resource, and state data. This creates a chain where modifying any entry invalidates all subsequent hashes. You can verify chain integrity via the API: ```bash curl -X POST https://conto.finance/api/audit-logs/verify \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "startDate": "2026-01-01T00:00:00Z", "endDate": "2026-04-07T00:00:00Z" }' ``` Response: ```json { "valid": true, "totalLogs": 1247, "verifiedLogs": 1247 } ``` If tampering is detected, the response includes `brokenAt` with the ID of the first compromised entry. ## Viewing Logs ### Dashboard Go to **Audit Logs** in the sidebar. Filter by: - Action type - Resource type - Actor (user or API key) - Date range ### API ```bash curl "https://conto.finance/api/audit-logs?resource=agent&action=SUSPEND&limit=20" \ -H "Authorization: Bearer $CONTO_ORG_KEY" ``` ## IP Attribution Audit logs capture the client IP address using platform-set headers in this priority order: 1. `cf-connecting-ip` (Cloudflare) 2. `x-real-ip` (nginx) 3. `x-forwarded-for` first entry (Vercel) This allows tracing actions back to specific network locations for security investigations. --- # Auto-Freeze Conto can automatically freeze agents when suspicious behavior is detected. When an agent is frozen, all its transactions are blocked until a human reviews and unfreezes it. ## How It Works After every transaction or policy evaluation, Conto checks the agent's behavior against configurable thresholds. If a threshold is exceeded: - **Auto-freeze enabled**: The agent is immediately suspended, its wallets are frozen, and a CRITICAL alert is created. - **Auto-freeze disabled** (default): An alert is created for visibility, but the agent continues operating. ## Trigger Types | Trigger | What it detects | Default threshold | |---------|----------------|-------------------| | `CONSECUTIVE_VIOLATIONS` | Repeated policy denials | 5 consecutive violations | | `CONSECUTIVE_FAILURES` | Repeated transaction failures | 5 consecutive failures | | `SPEND_VELOCITY` | Hourly spend rate vs. 30-day average | 3x the average | | `LARGE_TRANSACTION_ANOMALY` | Single transaction much larger than average | 10x the average | | `TRUST_SCORE_BELOW_THRESHOLD` | Average counterparty trust too low | Below 0.2 | | `TRUST_SCORE_DROP` | Trust score dropped significantly in 24h | 30% drop | | `RAPID_COUNTERPARTY_SWITCHING` | Too many unique recipients in one hour | 10 unique recipients/hour | | `MANUAL` | Admin manually froze the agent | N/A | ### Trigger Details **Spend Velocity** compares the agent's spend in the last hour against its average hourly spend over 30 days. Requires at least 5 historical transactions to activate. **Large Transaction Anomaly** compares the most recent transaction amount to the historical average. Requires at least 10 historical transactions. **Rapid Counterparty Switching** counts distinct `toAddress` values in the last hour. A sudden spike in new recipients can indicate compromised credentials. ## Configuration Configure thresholds per agent in the dashboard under **Agents > [Agent] > Freeze Settings**, or via the Admin SDK: ```typescript // Via Admin SDK await admin.agents.update(agentId, { autoFreezeEnabled: true, freezeConfig: { maxConsecutiveViolations: 3, maxConsecutiveFailures: 3, spendVelocityMultiplier: 2, largeTxMultiplier: 5, minTrustScore: 0.3, trustScoreDropThreshold: 0.2, maxNewCounterpartiesPerHour: 5, } }); ``` ### Configuration Fields | Field | Type | Default | Description | |-------|------|---------|-------------| | `maxConsecutiveViolations` | number | 5 | Freeze after N consecutive policy violations | | `maxConsecutiveFailures` | number | 5 | Freeze after N consecutive transaction failures | | `spendVelocityMultiplier` | number | 3 | Freeze when hourly spend exceeds Nx the 30-day average | | `largeTxMultiplier` | number | 10 | Freeze when a single tx exceeds Nx the historical average | | `minTrustScore` | number | 0.2 | Freeze when average counterparty trust drops below this | | `trustScoreDropThreshold` | number | 0.3 | Freeze on a 30%+ trust score drop in 24h | | `maxNewCounterpartiesPerHour` | number | 10 | Freeze when unique recipients per hour exceeds this | ## Freezing and Unfreezing ### Manual Freeze ```bash curl -X POST https://conto.finance/api/agents/AGENT_ID/freeze \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "reason": "Investigating unusual spending pattern", "freezeWallets": true }' ``` ### Manual Unfreeze ```bash curl -X POST https://conto.finance/api/agents/AGENT_ID/unfreeze \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "reason": "Investigation complete, no issues found", "unfreezeWallets": true, "resetCounters": true }' ``` Setting `resetCounters: true` (the default) resets the consecutive violation and failure counters so the agent doesn't immediately re-trigger after unfreezing. ## What Happens When an Agent Is Frozen 1. Agent status changes to `SUSPENDED` 2. All linked wallets are optionally frozen (`freezeWallets: true`) 3. A `CRITICAL` severity alert is created 4. A freeze event is recorded in the audit log 5. Webhooks fire (`agent.frozen`) 6. All subsequent payment requests return a denial ## Freeze History View freeze events in the dashboard under **Agents > [Agent] > Freeze History**, or via the API: ```bash curl https://conto.finance/api/freeze-events?agentId=AGENT_ID \ -H "Authorization: Bearer $CONTO_ORG_KEY" ``` Each freeze event records: - Trigger type and trigger data - Who initiated it (user or system) - Whether wallets were frozen - Resolution timestamp and notes (after unfreezing) --- # Card Management This guide walks through connecting payment cards in the Conto dashboard, assigning them to agents, and enforcing spend policies. Info: **Stripe Issuing** and **Lithic** card provisioning is coming soon. Currently, you can connect any existing card manually and enforce the full policy engine on it. ## Prerequisites - A Conto organization with at least one agent ## Connecting a Card Go to **Cards** in the sidebar. Click **Create Card**. The **Manual** provider is selected by default. Fill in: - **Card name** - A descriptive name (e.g., "Marketing Agent Card") - **Last 4 digits** - The last 4 digits of the card number - **Brand** - Visa, Mastercard, Amex, or Other - **Card type** - Virtual or Physical - **Daily limit** - Maximum daily spend (default: $1,000) - **Per-transaction limit** - Maximum per charge (default: $500) Note: Stripe Issuing and Lithic are shown in the provider list but marked as **Coming Soon**. When available, they will allow you to issue new cards directly through Conto. The card appears in your cards grid. You can now assign it to an agent. ## Assigning Cards to Agents Each card can be assigned to one or more agents with independent spend controls. On the card tile, click the menu icon and select **Assign to Agent**. Choose an active agent from the dropdown. Agents already linked to this card are filtered out. Configure per-agent spending controls: | Setting | Default | Description | |---------|---------|-------------| | Per-transaction | $250 | Max amount per charge | | Daily | $500 | Max daily spend | | Weekly | (optional) | Max weekly spend | | Monthly | (optional) | Max monthly spend | Agent limits cannot exceed the card's own limits. Click **Assign Card**. The agent can now use this card subject to the configured limits. ## Linking Policies Beyond field-based limits, you can link named policies to cards for advanced rule enforcement. Click on a card to open its detail page. In the **Policies** panel, you can see currently linked policies. Select a policy from the dropdown and click the add button. The policy's rules are immediately enforced on all card transactions. ### Example: Geographic + MCC Policy Create a policy with these rules, then link it to a card: ```json { "name": "US Merchants Only - No Gambling", "policyType": "CUSTOM", "priority": 50, "rules": [ { "ruleType": "GEOGRAPHIC_RESTRICTION", "operator": "NOT_IN", "value": "[\"US\", \"CA\"]", "action": "DENY" }, { "ruleType": "CARD_BLOCKED_MCCS", "operator": "IN", "value": "[\"7995\", \"7994\"]", "action": "DENY" } ] } ``` This denies any transaction from outside the US/Canada and blocks gambling MCCs. ## Card Lifecycle | Action | Dashboard | API | |--------|-----------|-----| | **Pause** | Menu > Pause Card | `PATCH /api/cards/{id}/state` | | **Resume** | Menu > Resume Card | `PATCH /api/cards/{id}/state` | | **Cancel** | Menu > Cancel Card | `PATCH /api/cards/{id}/state` | Cancelling a card deactivates all agent links. ## Monitoring The card detail page shows: - **Spend stats** - Spent today, per-tx limit, weekly/monthly limits - **Linked agents** - Each agent's individual limits and status - **Policies** - Linked policies with rule badges - **Transactions** - Recent card transactions with merchant details and status Spend tracking resets automatically at the configured intervals (daily, weekly, monthly). ## Roadmap | Feature | Status | |---------|--------| | Manual card connection | Available | | Spend policies + MCC controls | Available | | Card policy linking | Available | | Stripe Issuing provisioning | Coming soon | | Lithic provisioning | Coming soon | | Provider card import | Coming soon | | Provider state sync | Coming soon | ## Next Steps ### Card Payments API Link: https://conto.finance/docs/sdk/card-payments Full API reference for card operations ### Policy Overview Link: https://conto.finance/docs/policies/overview Learn about the policy engine ### Securing Agents Link: https://conto.finance/docs/guides/securing-agents Security best practices ### Policy Testing Link: https://conto.finance/docs/guides/policy-testing Test policies before deployment --- # Choose Your Integration If you are deciding between the Conto SDK, the OpenClaw skill, the Hermes skill, x402, and MPP, start here. The right path usually comes down to three questions: 1. Where does your agent run? 2. Who holds the wallet keys? 3. Are you making one-off payments or repeated protocol-native API payments? ## Quick Decision Matrix | Situation | Best fit | Why teams pick it | Start here | | ------------------------------------------------------------------------- | ------------------------ | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | | You are building your own backend, tool runner, or agent service | **Conto SDK / REST API** | Maximum control over payment, policy, and approval flows | [/sdk/installation](https://conto.finance/docs/sdk/installation) | | You already run agents in OpenClaw | **OpenClaw Skill** | Fastest path to policy enforcement inside the OpenClaw ecosystem | [/sdk/openclaw](https://conto.finance/docs/sdk/openclaw) | | You already run agents in Hermes | **Hermes Skill** | Native well-known skill install and natural-language policy operations | [/sdk/hermes](https://conto.finance/docs/sdk/hermes) | | You want operator workflows from Claude or another MCP client | **MCP Server** | Gives humans and assistant tools a shared control plane for agents, policies, and trust checks | [/mcp/overview](https://conto.finance/docs/mcp/overview) | | Your agent pays for APIs one request at a time | **x402** | Best fit for pay-per-call API commerce with explicit `402 Payment Required` flows | [/guides/x402-api-payments](https://conto.finance/docs/guides/x402-api-payments) | | Your agent will make many charges against one service in a single session | **MPP** | Better economics and ergonomics for repeated or streaming usage | [/guides/mpp-session-payments](https://conto.finance/docs/guides/mpp-session-payments) | | You need human review, dual control, or escalation | **Approval workflows** | Adds four-eyes review without blocking every low-risk payment | [/guides/approval-workflows](https://conto.finance/docs/guides/approval-workflows) | | You need to route based on recipient risk and reputation | **Trust scoring** | Adds counterparty-aware controls before money leaves the wallet | [/guides/trust-scoring](https://conto.finance/docs/guides/trust-scoring) | ## Choose the Control Surface First ### Conto SDK / REST API Choose the SDK or REST API when you own the agent runtime and want the most direct integration. - Best for custom backends, agent orchestration services, LangChain/OpenAI wrappers, and custom tools. - Works well with both managed wallets and agent-held external wallets. - Gives you the cleanest path to custom approval handling, policy creation, analytics, and audit automation. ### OpenClaw Skill Choose OpenClaw when your agent already lives inside OpenClaw and typically uses an external wallet or wallet MCP tools. - Install is fast through ClawHub (`npx clawhub install conto`). - The most common pattern is `approve -> transfer -> confirm`. - Requires `curl`, `jq`, and `python3` on `PATH` for the `conto-check.sh` helper. - Best when you want Conto to be the policy gate while your existing OpenClaw wallet stack keeps execution. ### Hermes Skill Choose the [Nous Hermes](https://hermes-agent.nousresearch.com/) skill when your agent is already running on Hermes and you want Conto policy enforcement to feel native in that workflow. - Installs through Hermes well-known skill discovery (`hermes skills install well-known:...`). - Good fit for natural-language policy management and wallet-aware agent operations. - Requires `curl`, `jq`, and `python3` on `PATH` for the `conto-check.sh` helper. - Supports the same underlying Conto approval and policy engine as the SDK flow. ### MCP Server Choose MCP when a human operator, analyst, or assistant needs to inspect trust, policies, alerts, and agent state alongside the runtime integrations above. - Good fit for finance, ops, and security teams. - Complements SDK, OpenClaw, and Hermes rather than replacing them. ## Then Choose the Payment Rail | Rail | Best for | Typical execution pattern | Primary controls | | ---------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------- | | **Standard onchain payment** | Vendor payouts, treasury actions, direct wallet transfers | `request -> execute` or `approve -> confirm` | Spend limits, approvals, trust rules, time windows | | **x402** | Paid APIs where each request is separately priced | `402 challenge -> pre-authorize -> pay -> retry -> record` | Price ceilings, service allowlists, endpoint velocity, service budgets | | **MPP** | Repeated requests to one service, streaming, session-based work | `pre-authorize -> open session -> charge -> close -> record` | Session budgets, max concurrent sessions, max duration, allowed methods | Info: You can mix these choices. For example, a Hermes or OpenClaw agent can still use x402 or MPP; the framework choice decides the control surface, while x402 and MPP decide the payment rail. ## Wallet Model: Integrated vs External | Wallet model | Best fit | What changes | Can Conto block a direct spend outside Conto? | | ------------------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------- | --------------------------------------------- | | **Integrated wallet** (`PRIVY` or `SPONGE`) | Teams that want Conto to orchestrate execution after policy approval | Usually one Conto call can both authorize and execute | **Yes** | | **External wallet** (`EXTERNAL`) | Agents that already control their own keys or use external wallet tools | Agent asks Conto for approval, executes transfer itself, then confirms back | **No** | If your agent already has wallet tools in OpenClaw or Hermes, the external model is often the fastest adoption path. If you want fewer moving parts, integrated wallets are usually the cleanest production setup. Note: External wallets can still use the same policy engine, approval workflows, and audit trail. The difference is that Conto governs the flow that goes through Conto. It does not cryptographically block a direct transfer your signer makes outside Conto. ## Canonical Examples ### 1. Custom agent with managed execution - Runtime: custom backend - Wallet model: integrated - Rail: standard onchain payment - Flow: `POST /api/sdk/payments/request` with `autoExecute: true` - Best for: vendor payments, infra spend, low-latency production flows ### 2. OpenClaw agent with external wallet controls - Runtime: OpenClaw - Wallet model: external - Rail: standard onchain payment - Flow: `POST /api/sdk/payments/approve -> transfer -> POST /api/sdk/payments/{id}/confirm` - Best for: teams that already have wallet MCP tools and want to add guardrails without re-architecting ### 3. Hermes agent paying APIs with x402 - Runtime: Hermes - Wallet model: usually external - Rail: x402 - Flow: `402 challenge -> /api/sdk/x402/pre-authorize -> pay -> retry -> /api/sdk/x402/record` - Best for: controlled pay-per-call API buying ### 4. SDK integration for high-frequency MPP sessions - Runtime: custom backend - Wallet model: integrated or external - Rail: MPP - Flow: `pre-authorize deposit -> open session -> repeated charges -> close session -> record settlement` - Best for: repeated calls to the same service where per-call onchain settlement would be wasteful ## Decision Tree ```mermaid flowchart TD A["Where does the agent run?"] --> B["OpenClaw"] A --> C["Hermes"] A --> D["Custom runtime"] A --> E["Operator assistant / MCP client"] B --> F["Use OpenClaw skill"] C --> G["Use Hermes skill"] D --> H["Use Conto SDK or REST API"] E --> I["Use MCP server"] F --> J["Who holds keys?"] G --> J H --> J J --> K["Integrated wallet"] J --> L["External wallet"] K --> M["Prefer request + autoExecute"] L --> N["Prefer approve + confirm"] M --> O["Need paid API calls?"] N --> O O --> P["One request at a time: x402"] O --> Q["Many requests in one session: MPP"] O --> R["Direct transfers: standard onchain flow"] ``` ## Related Guides ### Architecture Patterns Link: https://conto.finance/docs/guides/architecture-patterns See the core payment, approval, x402, and MPP diagrams ### Recipes Link: https://conto.finance/docs/guides/recipes Copy-paste commands for SDK, OpenClaw, Hermes, x402, MPP, approvals, and trust ### Approval Workflows Link: https://conto.finance/docs/guides/approval-workflows Add four-eyes review and escalation paths ### Trust Scoring Link: https://conto.finance/docs/guides/trust-scoring Understand counterparty risk, verification, and trust-based controls --- # Conto Hybrid Integration Use this pattern when your app already owns the runtime, wallet flow, or provider settlement path and you want Conto to enforce policy before money moves. In a hybrid integration: - your app decides when a paid action is about to happen - Conto approves, denies, or routes that payment to review - your app executes the actual settlement - your app records the final spend back to Conto ## What hybrid means In managed execution, Conto both authorizes and orchestrates payment through an integrated wallet. In hybrid execution, Conto stays in the control plane while your app stays in the execution path. That makes hybrid a strong fit when you already have: - an existing wallet or treasury layer - custom routing or fallback logic - provider-specific settlement code - x402 or MPP payment handling in your own runtime The main rule is simple: Conto should make the authorization decision before your app executes the payment. If your app settles first and reports later, Conto becomes an analytics mirror instead of a real spend-control layer. ## When to use hybrid Choose hybrid when: - you want to keep your current runtime architecture - paid actions are embedded inside a larger workflow - you need policy checks before API calls, machine payments, or provider settlements - you want Conto controls without moving all execution into Conto-managed wallets Hybrid is especially useful for agent platforms, AI orchestration products, marketplaces, and machine-payment systems. ## Responsibility split ### Your app owns - the user experience - workflow and routing logic - provider selection and fallback behavior - execution timing - the settlement call itself - app-specific state and reporting ### Conto owns - spend policies - approval thresholds - allowlists and counterparties - budget controls - runtime authorization - audit visibility - spend analytics ## Canonical flow ```mermaid flowchart LR App["Your app"] --> Context["Build payment context"] Context --> Authorize["Conto authorization"] Authorize --> Gate{"Decision"} Gate -->|"Approved"| Execute["Your app executes payment"] Gate -->|"Requires review"| Review["Approval workflow"] Gate -->|"Denied"| Stop["Do not settle"] Review -->|"Approved"| Execute Review -->|"Rejected"| Stop Execute --> Record["Record final spend back to Conto"] Record --> Audit["Audit logs, analytics, budgets"] ``` ## Recommended integration steps 1. Keep the product-facing controls in your app. 2. Provision a Conto agent for each managed actor. 3. Create and protect a runtime credential for that actor. 4. Sync supported controls into Conto policy objects. 5. Call Conto before the paid action executes. 6. Execute the payment in your own runtime only if approved. 7. Record the settled spend back to Conto. ## Common policy mappings | Product control | Conto primitive | | --- | --- | | Max spend per action | `SPEND_LIMIT` | | Budget per session or run | `BUDGET_ALLOCATION` | | Allowed providers or vendors | `COUNTERPARTY` or `WHITELIST` | | Human review threshold | `APPROVAL_THRESHOLD` | | Time-based restrictions | Time or blackout-related policy types | ## Verification checklist Use this checklist after setup: - each managed actor gets a dedicated Conto binding - runtime credentials stay server-side and encrypted at rest - policy changes update Conto objects - pre-authorization happens before settlement - denied payments never reach the execution layer - successful payments are recorded back to Conto - operators can see runtime status and recent events ## Related guides ### Choose Your Integration Link: https://conto.finance/docs/guides/choose-your-integration Compare SDK, OpenClaw, Hermes, x402, and MPP paths ### Architecture Patterns Link: https://conto.finance/docs/guides/architecture-patterns See the core managed, external wallet, x402, and MPP diagrams ### x402 API Payments Link: https://conto.finance/docs/guides/x402-api-payments Govern pay-per-call API payments before execution ### MPP Session Payments Link: https://conto.finance/docs/guides/mpp-session-payments Control repeated charges inside a session budget --- # Custody Modes Before you link a wallet, decide which custody mode you want. In Conto, these are two separate questions: 1. **Custody**. Who can sign the transfer? 2. **Enforcement**. Can Conto actually stop the transfer, or only govern the path that goes through Conto? ## Quick Comparison | Mode | Keys held by | Typical flow | Can Conto block a direct spend signed outside Conto? | Best for | | -------------------------------------- | ------------------------------------- | ---------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------ | | **Managed** (`PRIVY`, `SPONGE`) | The custody provider | `request -> execute` | **Yes.** Conto stays in the execution path. | Teams that want Conto-orchestrated execution | | **External / Watch-Only** (`EXTERNAL`) | You, your agent, or your wallet stack | `approve -> transfer -> confirm` | **No.** Conto only controls payments routed through Conto. | Self-custody, MPC wallets, existing wallet tools | | **Smart Contract** (`SMART_CONTRACT`) | Onchain wallet contract | `request -> contract execute -> confirm` | **Yes.** Transfers require the contract + Conto signature path. | Strongest onchain enforcement model | ## What Policies Mean In Each Mode ### Managed Wallets With `PRIVY` or `SPONGE`, the wallet provider holds the keys and Conto stays in the execution path. This is the strongest standard setup for policy enforcement. - Conto evaluates the payment. - If approved, Conto orchestrates execution. - If denied, the payment does not execute through Conto. - The agent uses the standard `request -> execute` flow. For `PRIVY`, you now have two setup options: - **Create a new Privy wallet through Conto** and let Conto mint the wallet record for you. - **Attach an existing Privy-backed wallet** by creating the Conto wallet with `custodyType=PRIVY`, `externalWalletId=`, and `address=`. Attach requests are idempotent per `externalWalletId + chainId` or `address + chainId` inside the organization, so you can safely retry provisioning calls from your backend. ## External / Watch-Only Wallets When you import a wallet, Conto registers it as `EXTERNAL` custody. You keep the keys, and your agent or wallet stack signs the transaction. - Conto can still evaluate policies before the transfer. - Conto can still require approval, record the payment, and keep the audit trail. - The agent uses the `approve -> transfer -> confirm` flow. - Conto **cannot cryptographically block** a direct transfer signed outside Conto. You can register an external wallet directly with `custodyType=EXTERNAL` plus `address`, or use the existing watch-only import flow. Both end up as `EXTERNAL` custody inside Conto. That means external wallets are best described as **self-custody with policy gating**, not as fully Conto-controlled execution. External wallets can have policies. Those policies are enforced when the payment goes through Conto. They are not a guarantee against a direct self-signed transfer outside Conto. ## Smart Contract Wallets Smart contract wallets are the strongest enforcement option. - Funds stay in an onchain contract wallet. - Transfers require the approved contract path and Conto's signature. - This gives you cryptographic enforcement without giving Conto direct key custody. ## Which Flow Should My Agent Use? | Wallet model | Endpoints to use | | ------------------------------------- | ------------------------------------------------------------------------------------------------------------- | | **Managed** (`PRIVY`, `SPONGE`) | `POST /api/sdk/payments/request` then `POST /api/sdk/payments/{id}/execute` | | **External** (`EXTERNAL`) | `POST /api/sdk/payments/approve` then your signer/wallet transfer, then `POST /api/sdk/payments/{id}/confirm` | | **Smart Contract** (`SMART_CONTRACT`) | Request approval, execute through the contract path, then confirm | ## How To Choose - Pick **Managed** if you want the cleanest production path and want Conto to stay in the execution flow. - Pick **External** if you already have a wallet stack, MPC signer, or agent-held wallet and want to keep that setup. - Pick **Smart Contract** if you want the strongest onchain enforcement model. ## Related Guides ### Choose Your Integration Link: https://conto.finance/docs/guides/choose-your-integration Compare SDK, OpenClaw, Hermes, x402, and MPP ### Payments API Link: https://conto.finance/docs/sdk/payments See the managed and external payment flows --- # External Approval Channels This guide walks through setting up approval notifications so your team can approve or reject payments from wherever they already work. ## Prerequisites An active organization with at least one agent An approval workflow configured ([Securing Agents](https://conto.finance/docs/guides/securing-agents)) Admin or Owner role in your organization ## What You'll Build By the end of this guide, your approval flow will look like this: ``` Agent requests $500 payment → Approval workflow triggers → Approver gets a Slack message with Approve/Reject buttons → Approver clicks "Approve" in Slack → Payment is approved and executed → Audit log records: who, when, which channel ``` ## Step 1: Set Up an Approval Workflow If you haven't already, create an approval workflow that triggers on the conditions you care about. Go to **Policies** in the dashboard. Create a new Approval Workflow with: | Field | Example Value | |-------|--------------| | Name | Large Payment Review | | Amount Threshold | $100 | | Required Approvals | 1 | | Timeout | 24 hours | | Approver Roles | OWNER, ADMIN | Request a payment above your threshold and confirm it enters `PENDING_APPROVAL` status. ## Step 2: Add a Notification Channel Channels"> Click **Add Channel** and choose your platform. Fill in the platform-specific fields. **For Slack:** - Bot Token: `xoxb-...` (from your Slack app) - Channel ID: `C0123456789` **For Email:** - Recipients: `cfo@company.com, finance@company.com` **For Telegram:** - Bot Token: from @BotFather - Chat ID: your group chat ID At minimum, enable `approval.requested`. Consider also enabling: - `approval.escalated` for time-sensitive visibility - `approval.expired` so nothing falls through the cracks Click the test button. You should receive a sample notification on your platform. ## Step 3: Test the Full Flow Use the SDK or dashboard to request a payment above your approval threshold. ```bash curl -X POST https://conto.finance/api/sdk/payments/request \ -H "Authorization: Bearer $SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 500, "recipientAddress": "0x...", "purpose": "Test approval" }' ``` Within a few seconds, you should receive a notification on your configured channel with payment details and Approve/Reject buttons. Complete the approval from the email confirmation page or the Conto dashboard. The payment should move to `APPROVED` status and execute automatically if you are using Conto-managed wallets. Go to **Audit Logs** in the dashboard. You should see the approval decision with the channel recorded (e.g., `SLACK`, `EMAIL`). ## Multiple Channels You can configure multiple channels simultaneously. When an approval request is created, notifications are sent to all active channels that subscribe to the `approval.requested` event. The first approver to act from any channel resolves the request. Info: Each channel delivers independently via background jobs. If one channel fails (e.g., Slack API is down), other channels still deliver. Failed deliveries retry up to 3 times with exponential backoff. ## Webhook Integration for Custom Systems If you already have your own approval system, use the Webhook channel type to receive approval requests and hand the final decision off to an authenticated Conto approver. ```bash # 1. Configure a webhook channel pointing to your system # 2. Your system receives a signed payload with `eligibleApprovers` and `dashboardUrl` # 3. Notify the right approver or open your own review UI # 4. Send the user to `dashboardUrl` to complete the approval in Conto ``` The response confirms the decision: ```json { "success": true, "message": "Payment request approved.", "finalStatus": "APPROVED", "channel": "WEBHOOK" } ``` ## Troubleshooting 1. Check that the channel is **active** (toggle is on) in Settings > Channels 2. Check the `lastError` field on the channel for API errors 3. Verify your bot token or credentials are correct 4. For Slack, confirm the bot is invited to the target channel 5. For Telegram, confirm the webhook URL is set correctly Action tokens are one-time use and expire with the approval request. If the approval request has already been resolved (by another approver or from the dashboard), the token is no longer valid. Confirm your Slack app's interactivity request URL is set to `https://conto.finance/api/webhooks/slack` and that `SLACK_SIGNING_SECRET` is configured in your environment. ## Next Steps ### Channel Reference Link: https://conto.finance/docs/integrations/notification-channels Full configuration reference for all channel types ### Policy Testing Link: https://conto.finance/docs/guides/policy-testing Test your approval workflows before going to production --- # Your First Agent Payment This guide walks you through connecting an agent, linking a wallet, and making your first onchain payment — end to end. By the end, you'll have a working agent that can request and execute stablecoin transfers. ## Prerequisites A Conto account ([sign up](https://conto.finance)) Access to the Conto dashboard ## What You'll Build ``` Wallet → Agent → Wallet Link → SDK Key → Payment Request → Onchain Transfer ``` A fully configured agent that can autonomously request payments, have them checked against a spending policy, and execute approved transfers on Tempo Testnet. ## Step 1: Create a Wallet Go to **Wallets** in the sidebar and click **Create Wallet**. | Field | Value | |-------|-------| | Name | My First Wallet | | Chain Type | EVM | | Network | Tempo Testnet | | Custody | SPONGE (recommended — managed keys via [@paysponge/sdk](https://docs.paysponge.com/sdk-reference), no setup) | If you already have a wallet outside Conto: - Use `custodyType=PRIVY` with `externalWalletId` and `address` to attach an existing Privy-backed wallet. - Use `custodyType=EXTERNAL` with `address` to register a self-custodied wallet. Choose the existing-wallet path when you want the same signer to be reused across your app and Conto instead of creating a fresh wallet. Click **Provision** to generate an onchain address. This creates a wallet managed by Sponge's custody infrastructure. For Tempo Testnet, get free testnet tokens from the Tempo faucet or use the **Faucet** button on the wallet detail page. Info: Tempo Testnet uses **pathUSD** — a test stablecoin that costs nothing. You can experiment freely without risking real funds. ## Step 2: Connect an Agent Go to **Agents** in the sidebar and click **Connect Agent**. | Field | Value | |-------|-------| | Name | My First Agent | | Agent Type | CUSTOM (or choose your framework) | In the wallet linking step, select "My First Wallet" and configure spending limits: | Setting | Value | |---------|-------| | Delegation Type | Limited | | Per Transaction | `100` | | Daily Limit | `500` | Do not set **Per Transaction** to `0`. A zero value blocks all payments at the wallet level before policies are even evaluated. Finish setup. Your agent is now active with a linked, funded wallet. ## Step 3: Generate an SDK Key Click your agent name to open its detail page. Click the **SDK Integration** tab. Click **Generate SDK Key**. Copy it immediately — it's only shown once. ```bash export CONTO_API_KEY="conto_agent_your_key_here" ``` ## Step 4: Verify Your Setup Before making a payment, confirm everything is wired up correctly: ```bash curl https://conto.finance/api/sdk/setup \ -H "Authorization: Bearer $CONTO_API_KEY" ``` Check the response: - `agent.status` is `"ACTIVE"` - `wallets` array contains your wallet - The wallet shows a non-zero balance ## Step 5: Request a Payment ```bash curl -X POST https://conto.finance/api/sdk/payments/request \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 1, "recipientAddress": "0x1234567890abcdef1234567890abcdef12345678", "purpose": "My first Conto payment", "category": "TESTING" }' ``` Expected response: ```json { "requestId": "cmm...", "status": "APPROVED", "currency": "pathUSD", "wallet": { "address": "0x...", "availableBalance": 999.00 } } ``` The `requestId` is your handle for executing this payment. ## Step 6: Execute the Payment Take the `requestId` from the previous response and execute it onchain: ```bash curl -X POST https://conto.finance/api/sdk/payments/REQUEST_ID/execute \ -H "Authorization: Bearer $CONTO_API_KEY" ``` Replace `REQUEST_ID` with your actual request ID. The response includes: - `txHash` — the onchain transaction hash - `explorerUrl` — link to verify on [explore.tempo.xyz](https://explore.tempo.xyz) ## Step 7: Verify Onchain Three ways to confirm your payment went through: 1. **Explorer** — Click the `explorerUrl` from the execute response 2. **Dashboard** — Go to **Transactions** and find your payment with status "Confirmed" 3. **API** — Query the transaction status: ```bash curl https://conto.finance/api/sdk/payments/REQUEST_ID/status \ -H "Authorization: Bearer $CONTO_API_KEY" ``` ## Using the TypeScript SDK The same flow using the SDK: ```typescript import { Conto } from '@conto/sdk'; const conto = new Conto({ apiKey: process.env.CONTO_API_KEY }); // Request payment const request = await conto.payments.request({ amount: 1, recipientAddress: '0x1234567890abcdef1234567890abcdef12345678', purpose: 'My first Conto payment', }); console.log('Status:', request.status); // APPROVED // Execute onchain if (request.status === 'APPROVED') { const result = await conto.payments.execute(request.requestId); console.log('TX Hash:', result.txHash); console.log('Explorer:', result.explorerUrl); } ``` ## What Just Happened Here's the full flow you completed: | Step | What Conto Did | |------|----------------| | Payment request | Authenticated your SDK key, identified the agent | | Policy check | Evaluated wallet spending limits ($1 is under the $100 per-tx limit) | | Wallet selection | Picked the linked wallet with sufficient balance | | Execution | Signed and submitted a pathUSD transfer on Tempo Testnet | | Confirmation | Verified the transaction onchain and logged it to audit trail | ## Troubleshooting Your SDK key may be invalid or expired. Generate a new one from the agent detail page under **SDK Integration**. Your testnet wallet needs funding. Use the Tempo faucet or the **Faucet** button on the wallet detail page. The wallet-level per-transaction limit is `0`. Edit the wallet limits on the agent detail page (pencil icon) and set it to a non-zero value. No wallet is linked to the agent, or the linked wallet doesn't have enough balance. Check the **Overview** tab on the agent detail page. ## Next Steps ### Test Policies Link: https://conto.finance/docs/guides/testing-payments Verify spending policies enforce correctly with test transactions ### Secure Your Agent Link: https://conto.finance/docs/guides/securing-agents Add spending limits, time windows, and counterparty controls ### Recipes Link: https://conto.finance/docs/guides/recipes Copy-paste solutions for common tasks ### SDK Reference Link: https://conto.finance/docs/sdk/payments Full payment API methods and parameters --- # MPP Session Payments The Machine Payment Protocol (MPP) enables session-based micropayments on the Tempo blockchain. Unlike x402 where each API call is a separate payment, MPP opens a session with a deposit budget — your agent makes multiple requests against that budget, and the session settles when done. This guide walks through the full session lifecycle: pre-authorize, open, charge, close, and track. ## Prerequisites An active agent with an SDK key ([Quickstart: Connect Agent & Wallet](https://conto.finance/docs/quickstart/first-agent)) A funded Tempo wallet with `pathUSD` on testnet/demo flows or `USDC.e` on mainnet An MPP-enabled service to call (or use the examples below to simulate) ## MPP vs x402 | | x402 | MPP | |---|---|---| | **Payment model** | Pay-per-request | Session with deposit | | **Best for** | Occasional API calls | Streaming, multi-request workflows | | **Chain** | Base (USDC), Tempo (pathUSD) | Tempo (pathUSD) | | **Settlement** | Immediate per call | On session close | | **Unused funds** | N/A | Returned to agent | Use x402 when each API call is independent. Use MPP when your agent will make many requests to the same service in a session (e.g., streaming data, iterative processing, chat APIs). ## How MPP Works ``` Pre-authorize → Open session (deposit) → Make requests → Close session → Settle ``` | Step | What happens | |------|-------------| | 1 | Agent calls an MPP-enabled service, gets a 402 challenge | | 2 | Agent pre-authorizes the session deposit through Conto policies | | 3 | Agent opens a session with a deposit budget | | 4 | Agent makes requests — each consumes part of the deposit | | 5 | Session closes — unused deposit is returned | | 6 | Agent records the final settled amount in Conto | ## Step 1: Set Up MPP Policies Go to **Policies** → **New Policy**. | Field | Value | |-------|-------| | Name | MPP Session Guardrails | | Policy Type | MPP_CONTROLS | **Rule 1: Cap session deposit** | Field | Value | |-------|-------| | Rule Type | MPP_SESSION_BUDGET | | Operator | LTE | | Value | 25 | | Action | ALLOW | No single session can have a deposit greater than $25. **Rule 2: Cap per-request charge** | Field | Value | |-------|-------| | Rule Type | MPP_MAX_PER_REQUEST | | Operator | LTE | | Value | 1.00 | | Action | ALLOW | No individual request within a session can charge more than $1.00. **Rule 3: Limit concurrent sessions** | Field | Value | |-------|-------| | Rule Type | MPP_MAX_CONCURRENT_SESSIONS | | Operator | LTE | | Value | 3 | | Action | ALLOW | Agent can have at most 3 open sessions at once. Go to the agent's **Permissions** tab and assign the policy. ## Step 2: Pre-Authorize the Session When your agent needs to open an MPP session, first check policies: ```bash curl -X POST https://conto.finance/api/sdk/mpp/pre-authorize \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 10.00, "recipientAddress": "0xServiceAddress", "resourceUrl": "https://api.service.com/stream", "serviceDomain": "api.service.com", "intent": "session", "depositAmount": 10.00 }' ``` ### If Authorized ```json { "authorized": true, "wallet": { "id": "wal_123", "address": "0xAgentWallet", "chainId": "4217", "availableBalance": 500.00 }, "reasons": ["Within MPP session budget", "Service domain allowed"] } ``` Proceed to open the session with the MPP-enabled service. ### If Denied ```json { "authorized": false, "reasons": ["MPP session deposit $10 exceeds budget cap $5"], "violations": [ { "type": "MPP_SESSION_BUDGET", "limit": 5.00, "current": 10.00, "message": "Session deposit exceeds MPP budget" } ] } ``` The agent should either request a smaller session or escalate. ## Step 3: Open the Session After Conto authorizes, open the MPP session with the service. The session deposit is locked onchain: ```typescript // Using Sponge MCP or your MPP client const session = await mppClient.openSession({ serviceUrl: 'https://api.service.com/stream', maxDeposit: '10.00', walletAddress: auth.wallet.address, }); console.log('Session ID:', session.sessionId); // Session ID: mpp_session_xyz ``` The deposit ($10 in this case) is committed. The agent can now make requests up to this amount. ## Step 4: Make Requests Each request to the MPP service consumes part of the deposit: ```typescript // Request 1: $0.50 charge const result1 = await mppClient.request({ sessionId: session.sessionId, url: 'https://api.service.com/stream/query', body: { query: 'market analysis for AAPL' }, }); // Remaining deposit: $9.50 // Request 2: $0.75 charge const result2 = await mppClient.request({ sessionId: session.sessionId, url: 'https://api.service.com/stream/summarize', body: { text: result1.data }, }); // Remaining deposit: $8.75 ``` The service tracks charges against the session deposit. No additional onchain transactions happen during the session — settlement occurs on close. ## Step 5: Close and Settle When your agent is done, close the session. Unused deposit is returned: ```typescript const settlement = await mppClient.closeSession({ sessionId: session.sessionId, }); console.log('Total charged:', settlement.totalCharged); // $1.25 console.log('Returned:', settlement.returned); // $8.75 console.log('TX Hash:', settlement.transactionHash); ``` | Session Summary | Amount | |-----------------|--------| | Initial deposit | $10.00 | | Total charged | $1.25 | | Returned to agent | $8.75 | ## Step 6: Record in Conto Record the settled amount so Conto can track spending: ```bash curl -X POST https://conto.finance/api/sdk/mpp/record \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 1.25, "recipientAddress": "0xServiceAddress", "transactionHash": "0xsettlement123...", "resourceUrl": "https://api.service.com/stream", "serviceDomain": "api.service.com", "sessionId": "mpp_session_xyz", "scheme": "mpp", "walletId": "wal_123", "chainId": "4217" }' ``` Record the **settled amount** ($1.25), not the deposit amount ($10.00). The settled amount is what was actually charged. Recording the deposit would overcount spending against your budgets. ## Step 7: Monitor Spending ### Check MPP Budget ```bash curl https://conto.finance/api/sdk/mpp/budget \ -H "Authorization: Bearer $CONTO_API_KEY" ``` ### View MPP Services ```bash curl https://conto.finance/api/sdk/mpp/services \ -H "Authorization: Bearer $CONTO_API_KEY" ``` ### Dashboard Go to **Analytics** to see MPP-specific metrics: session frequency, average session cost, per-service breakdown, and deposit utilization (how much of each deposit is actually used). ## Full Integration Example Complete TypeScript flow for an MPP session: ```typescript import { Conto } from '@conto/sdk'; const conto = new Conto({ apiKey: process.env.CONTO_API_KEY }); async function runMppSession(serviceUrl: string, queries: string[]) { const serviceDomain = new URL(serviceUrl).hostname; // 1. Pre-authorize with Conto const auth = await conto.mpp.preAuthorize({ amount: 10.00, recipientAddress: '0xServiceAddress', resourceUrl: serviceUrl, serviceDomain, intent: 'session', depositAmount: 10.00, }); if (!auth.authorized) { throw new Error(`MPP denied: ${auth.reasons.join(', ')}`); } // 2. Open session const session = await mppClient.openSession({ serviceUrl, maxDeposit: '10.00', }); // 3. Make requests const results = []; for (const query of queries) { const result = await mppClient.request({ sessionId: session.sessionId, url: `${serviceUrl}/query`, body: { query }, }); results.push(result.data); } // 4. Close and settle const settlement = await mppClient.closeSession({ sessionId: session.sessionId, }); // 5. Record in Conto await conto.mpp.record({ amount: settlement.totalCharged, recipientAddress: '0xServiceAddress', transactionHash: settlement.transactionHash, resourceUrl: serviceUrl, serviceDomain, sessionId: session.sessionId, scheme: 'mpp', walletId: auth.wallet.id, chainId: auth.wallet.chainId, }); return { results, cost: settlement.totalCharged }; } ``` ## MPP Policy Reference All available MPP-specific policy rules: | Rule Type | What It Controls | |-----------|-----------------| | `MPP_MAX_PER_REQUEST` | Max charge per individual request in a session | | `MPP_PRICE_CEILING` | Hard ceiling on any MPP payment | | `MPP_MAX_PER_ENDPOINT` | Budget per specific endpoint | | `MPP_MAX_PER_SERVICE` | Budget per service domain | | `MPP_SESSION_BUDGET` | Maximum deposit per session | | `MPP_MAX_SESSION_DEPOSIT` | Hard cap on session deposit | | `MPP_MAX_CONCURRENT_SESSIONS` | Max open sessions at once | | `MPP_MAX_SESSION_DURATION` | Maximum session length | | `MPP_ALLOWED_SERVICES` | Allowlist of service domains | | `MPP_BLOCKED_SERVICES` | Blocklist of service domains | See [Advanced Policies](https://conto.finance/docs/policies/advanced) for value formats and operators. ## Troubleshooting Your agent has too many open sessions. Close existing sessions before opening new ones, or increase the `MPP_MAX_CONCURRENT_SESSIONS` limit in your policy. The deposit is locked onchain when the session opens. If your wallet has $15 and you try to open a $20 session, it fails. Check your wallet balance and request a smaller deposit. The service determines final settlement. If charges seem wrong, check the session details with the service provider. Conto records whatever you report — make sure you're recording the settlement amount from the close response. MPP is currently supported only on the Tempo blockchain (chain ID 4217). For paid APIs on Base or Ethereum, use the [x402 protocol](https://conto.finance/docs/guides/x402-api-payments) instead. ## Next Steps ### x402 Payments Link: https://conto.finance/docs/guides/x402-api-payments Per-request API payments on Base and Tempo ### MPP SDK Reference Link: https://conto.finance/docs/sdk/mpp-payments Full API reference for MPP endpoints ### Advanced Policies Link: https://conto.finance/docs/policies/advanced All MPP policy rule types and value formats ### Recipes Link: https://conto.finance/docs/guides/recipes Copy-paste MPP recipes --- # Testing Spending Policies This guide walks through setting up an agent from scratch and testing that spending policies enforce correctly. By the end, you'll have an agent with policies that approve, require approval, or deny payments based on amount thresholds. ## Prerequisites A Conto account and organization Access to the Conto dashboard A funded wallet (Tempo Testnet `pathUSD`, Tempo Mainnet `USDC.e`, or mainnet `USDC` on Base/Solana) ## What You'll Build | Amount Range | Expected Result | Policy | |---|---|---| | $0 - $10 | **Approved** automatically | Under all thresholds | | $10 - $15 | **Requires approval** | Exceeds approval threshold | | $15+ | **Denied** | Exceeds max transaction amount | ## Step 1: Create a Wallet Go to **Wallets** in the sidebar and click **Create Wallet**. - **Name**: e.g., "Test Operations Wallet" - **Custody Type**: PRIVY (recommended - enterprise-grade key management) - **Chain Type**: EVM Click **Provision** to assign an onchain address. Your wallet is now ready to receive funds. For Tempo Testnet, get testnet `pathUSD` from the Tempo faucet or use the **Faucet** button on the wallet detail page. For mainnet, fund the wallet with the stablecoin for that chain: `USDC.e` on Tempo Mainnet or `USDC` on Base/Solana. ## Step 2: Connect an Agent Go to **Agents** in the sidebar and click **Connect Agent**. - **Name**: e.g., "Policy Test Agent" - **Agent Type**: Choose your framework (select CUSTOM if unsure) In Step 2 of the wizard, select the wallet you created and configure spending limits: | Setting | Value | |---|---| | Delegation Type | Limited | | Per Transaction | `200` | | Daily Limit | `1000` | | Weekly Limit | `5000` | | Monthly Limit | `20000` | Do not set **Per Transaction** to `0`. A zero value blocks all payments at the wallet level before policies are evaluated. This is the most common setup mistake. Finish the wizard. Your agent is now active with a linked wallet. ## Step 3: Generate an SDK Key Click on your agent to open its detail page. Click the **SDK Integration** tab. Click **Generate SDK Key**. Copy the key immediately - it is only shown once. The key looks like: `conto_agent_72015200221e...` Store it in an environment variable: ```bash export CONTO_API_KEY="conto_agent_your_key_here" ``` ## Step 4: Create Policies Create two policies to test different enforcement behaviors. ### Policy A: Spend Limit Go to **Policies** in the sidebar and click **Create Policy**. - **Name**: "Manual Spend Test" - **Description**: "Deny transactions over $15" - **Policy Type**: SPEND_LIMIT | Field | Value | |---|---| | Rule Type | `MAX_AMOUNT` | | Operator | `LTE` | | Value | `15` | | Action | `ALLOW` | This allows transactions up to $15. Anything above is denied. Info: **How rule actions work:** - `ALLOW` + `LTE $15` means: "Allow transactions where the amount is ≤ $15" - Transactions that do NOT match the ALLOW condition are implicitly denied - `DENY` + `GT $15` achieves the same effect: "Deny transactions where amount > $15" - `REQUIRE_APPROVAL` + `GT $10` means: "Require manual approval when amount > $10" Think of it as: the **action** tells Conto what to do when the **condition is true**. ### Policy B: Approval Threshold - **Name**: "Manual Approval Test" - **Description**: "Require approval for transactions over $10" - **Policy Type**: APPROVAL_THRESHOLD | Field | Value | |---|---| | Rule Type | `REQUIRE_APPROVAL_ABOVE` | | Operator | `GREATER_THAN` | | Value | `10` | | Action | `REQUIRE_APPROVAL` | Transactions over $10 require manual approval in the dashboard before they can execute. ## Step 5: Assign Policies to the Agent Navigate to your agent's detail page. Click the **Permissions** tab. Assign "Manual Spend Test" and "Manual Approval Test" to the agent. All assigned policies are evaluated with AND logic — the most restrictive rule wins. ## Step 6: Run Test Transactions Use `curl` or any HTTP client to test the three scenarios. ### Verify Your Setup Before running test transactions, verify your agent is correctly configured: ```bash curl https://conto.finance/api/sdk/setup \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` This returns your agent's profile, linked wallets, active policies, and current spend tracking. Check that: - `agent.status` is `"ACTIVE"` - `wallets` array is not empty - `policies` shows your assigned policies ### Test 1: $5 Payment (Expect: APPROVED) ```bash curl -X POST https://conto.finance/api/sdk/payments/request \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 5, "recipientAddress": "0x1234567890abcdef1234567890abcdef12345678", "purpose": "Test - under all limits", "category": "software" }' ``` Expected response: ```json { "requestId": "cmm...", "status": "APPROVED", "currency": "pathUSD", "wallet": { "address": "0x788dD0...", "availableBalance": 999822 } } ``` ### Test 2: $12 Payment (Expect: REQUIRES_APPROVAL) ```bash curl -X POST https://conto.finance/api/sdk/payments/request \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 12, "recipientAddress": "0x1234567890abcdef1234567890abcdef12345678", "purpose": "Test - over approval threshold" }' ``` Expected response: ```json { "requestId": "cmm...", "status": "REQUIRES_APPROVAL", "violations": [ { "type": "PER_TX_LIMIT", "message": "[Policy: Manual Approval Test] Amount $12 exceeds approval threshold $10", "source": "policy_rule", "policyName": "Manual Approval Test" } ] } ``` ### Test 3: $20 Payment (Expect: DENIED) ```bash curl -X POST https://conto.finance/api/sdk/payments/request \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 20, "recipientAddress": "0x1234567890abcdef1234567890abcdef12345678", "purpose": "Test - over max amount" }' ``` Expected response: ```json { "requestId": "cmm...", "status": "DENIED", "violations": [ { "type": "PER_TX_LIMIT", "message": "[Policy: Manual Approval Test] Amount $20 exceeds approval threshold $10", "source": "policy_rule" } ] } ``` ## Step 7: Execute an Approved Payment If Test 1 returned `APPROVED`, you can execute it onchain: ```bash curl -X POST https://conto.finance/api/sdk/payments/REQUEST_ID/execute \ -H "Authorization: Bearer $CONTO_API_KEY" ``` Response includes the `txHash` and an explorer URL to verify onchain. ## Using the SDK Instead of curl The same tests using the TypeScript SDK: ```typescript import { Conto } from '@conto/sdk'; const conto = new Conto({ apiKey: process.env.CONTO_API_KEY }); // Test 1: Should approve const test1 = await conto.payments.request({ amount: 5, recipientAddress: '0x1234567890abcdef1234567890abcdef12345678', purpose: 'Test - under all limits', }); console.log('Test 1:', test1.status); // APPROVED // Test 2: Should require approval const test2 = await conto.payments.request({ amount: 12, recipientAddress: '0x1234567890abcdef1234567890abcdef12345678', purpose: 'Test - over approval threshold', }); console.log('Test 2:', test2.status); // REQUIRES_APPROVAL // Test 3: Should deny const test3 = await conto.payments.request({ amount: 20, recipientAddress: '0x1234567890abcdef1234567890abcdef12345678', purpose: 'Test - over max amount', }); console.log('Test 3:', test3.status); // DENIED ``` ## Editing Wallet Limits After Setup If you need to change wallet spending limits after the initial setup: 1. Go to the agent detail page 2. Open the **Overview** or **Wallets** tab 3. Click the **pencil icon** next to the wallet 4. Update per-transaction, daily, weekly, or monthly limits 5. Click **Save Changes** If the per-transaction limit is set to `$0`, all payments will be denied at the wallet level with the message "Amount exceeds per-transaction limit of $0". Update it to a value that makes sense for your use case. ## How Policy Evaluation Works Conto evaluates wallet-level limits first, then policy rules with AND logic. The first DENY stops evaluation. Org-level policies stack with agent-level policies. See [Policy Overview](https://conto.finance/docs/policies/overview) for the full reference. ## Troubleshooting The wallet-level per-transaction limit is set to `0`. Edit the wallet limits on the agent detail page (pencil icon) and set it to a non-zero value. Multiple policies are evaluated with AND logic. If one policy denies while another would require approval, the denial takes priority. Check which policies are assigned in the Permissions tab. Policies at the same priority level are all evaluated. If your approval threshold (`>$10`) fires before your spend limit (`>$15`), it's because the approval threshold is checked first. Both are still enforced — the most restrictive outcome wins. SDK keys are only shown once when generated. If you've lost it, generate a new one from the agent detail page under **SDK Integration**. Org-level policies apply to all agents. If a Starter policy caps transactions at $25 and your agent policy allows up to $100, the $25 cap wins. Check with your org admin. ## Next Steps ### Policy Types Link: https://conto.finance/docs/policies/overview Explore all available policy rule types ### SDK Reference Link: https://conto.finance/docs/sdk/payments Full SDK payment methods and options ### Time Windows Link: https://conto.finance/docs/policies/time-windows Restrict payments to specific hours and days ### Counterparties Link: https://conto.finance/docs/policies/counterparties Allowlist and blocklist recipient addresses --- # Rate Limits All API endpoints are rate-limited using a sliding window algorithm. Limits are enforced per agent (for SDK endpoints) or per IP/user (for dashboard and auth endpoints). ## Limits by Endpoint | Endpoint Category | Limit | Window | Key | |------------------|-------|--------|-----| | SDK payment endpoints (`/request`, `/execute`, `/approve`, `/confirm`) | 60 requests | 1 minute | Per agent | | SDK read endpoints (`/wallets`, `/transactions`, `/policies`, `/setup`) | 120 requests | 1 minute | Per agent | | Auth endpoints (login, register) | 10 requests | 5 minutes | Per IP | | Auth endpoints (login, register) | 5 requests | 5 minutes | Per account | | Dashboard API endpoints | 100 requests | 1 minute | Per user | Info: Auth endpoints enforce **two independent limits**: per-IP and per-account. The per-account limit prevents credential stuffing attacks that rotate source IPs. Both limits must pass for a request to proceed. ## Response Headers Every API response includes rate limit headers: | Header | Description | |--------|-------------| | `X-RateLimit-Remaining` | Requests remaining in the current window | | `X-RateLimit-Reset` | ISO 8601 timestamp when the window resets | | `Retry-After` | Seconds to wait (only present when rate limited) | ## Rate Limited Response When a request is rate limited, the API returns HTTP `429`: ```json { "error": "Rate limit exceeded. Please slow down your requests.", "code": "RATE_LIMITED", "retryAfter": 12 } ``` ## Retry Strategy The SDK handles retries automatically for `429` and `5xx` responses: - Up to **3 retries** with exponential backoff - Respects `Retry-After` headers - Backoff: 1s, 2s, 4s (capped at 10s) - Client errors (`4xx` except `429`) are not retried For manual retry logic: ```typescript async function retryOnRateLimit(fn: () => Promise , maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { const response = await fn(); if (response.status === 429) { const retryAfter = parseInt(response.headers.get('Retry-After') || '5', 10); await new Promise(r => setTimeout(r, retryAfter * 1000)); continue; } return response; } throw new Error('Rate limited after max retries'); } ``` ## Backend Rate limiting uses Redis (Upstash) in production with a sliding window counter. In development without Redis, an in-memory fallback is used (not suitable for production multi-instance deployments). On Redis errors, the limiter **fails closed** (denies the request) to prevent abuse during outages. --- # Recipes Quick, self-contained solutions for specific tasks. Each recipe assumes you have an active agent with an SDK key. For initial setup, see [Connect Agent & Wallet](https://conto.finance/docs/quickstart/first-agent). ```bash # Agent SDK key (for payment operations) export CONTO_SDK_KEY="conto_agent_your_key_here" # Org API key (for admin operations: managing agents, wallets, policies) export CONTO_ORG_KEY="conto_your_org_key_here" ``` --- ## Agent Frameworks Both skill flavors below rely on `conto-check.sh`, which in turn requires `curl`, `jq`, and `python3` on `PATH`. `curl` and `python3` ship with macOS and most Linux distros; install `jq` via your package manager. `python3` powers the localhost OAuth callback used by `conto-check.sh setup`. ### Install the OpenClaw Skill ```bash npx clawhub install conto conto-check.sh setup "my-openclaw-agent" "0xYourWalletAddress" EVM 42431 ``` Use this when your OpenClaw agent already has wallet tools and you want Conto to become the policy gate. --- ### Install the Hermes Skill ```bash hermes skills install well-known:https://conto.finance/.well-known/skills/conto conto-check.sh setup "my-hermes-agent" "0xYourWalletAddress" EVM 42431 ``` Use this when you want Hermes-native skill installation with the same Conto policy and approval controls. --- ## Setup ### Connect an Agent via API ```bash curl -X POST https://conto.finance/api/agents \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "My Agent", "agentType": "CUSTOM", "status": "ACTIVE" }' ``` Returns the agent ID. Use an org-level API key (`conto_org_...`) for this call. --- ### Generate an SDK Key via API ```bash curl -X POST https://conto.finance/api/agents/AGENT_ID/sdk-keys \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Production Key", "scopes": ["payments:request", "payments:execute", "wallets:read", "transactions:read"] }' ``` Copy the key from the response — it's only shown once. --- ### Provision a Sponge Wallet Sponge custody uses the [`@paysponge/sdk`](https://docs.paysponge.com/sdk-reference) under the hood. Set `SPONGE_API_KEY` (and `SPONGE_MASTER_KEY` for fleet management) in your environment. ```bash curl -X POST https://conto.finance/api/wallets \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Agent Ops Wallet", "chainType": "EVM", "custodyType": "SPONGE" }' ``` Then provision it to generate an onchain address: ```bash curl -X POST https://conto.finance/api/wallets/WALLET_ID/provision \ -H "Authorization: Bearer $CONTO_ORG_KEY" ``` --- ### Link a Wallet to an Agent ```bash curl -X POST https://conto.finance/api/agents/AGENT_ID/wallets \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "walletId": "WALLET_ID", "delegationType": "LIMITED", "perTransactionLimit": 100, "dailyLimit": 500 }' ``` --- ### Check Agent Setup Verify the agent is correctly configured with wallets and policies: ```bash curl https://conto.finance/api/sdk/setup \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` --- ## Payments ### Request and Execute a Payment Two calls: request (policy check) → execute (onchain transfer). ```bash # Step 1: Request REQUEST=$(curl -s -X POST https://conto.finance/api/sdk/payments/request \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 10, "recipientAddress": "0x1234567890abcdef1234567890abcdef12345678", "purpose": "Service payment" }') echo $REQUEST # Step 2: Execute (extract requestId from response) curl -X POST https://conto.finance/api/sdk/payments/REQUEST_ID/execute \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` --- ### Check Transaction Status ```bash curl https://conto.finance/api/sdk/payments/REQUEST_ID/status \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` Returns `PENDING`, `CONFIRMING`, `CONFIRMED`, `FAILED`, or `REJECTED`. --- ### List Recent Transactions ```bash curl https://conto.finance/api/sdk/transactions \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` --- ### Pre-Authorize an x402 Payment ```bash curl -X POST https://conto.finance/api/sdk/x402/pre-authorize \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 0.05, "recipientAddress": "0xFacilitatorAddress", "resourceUrl": "https://api.example.com/data", "serviceDomain": "api.example.com" }' ``` Returns `"authorized": true` with wallet details, or `"authorized": false` with violation reasons. --- ### Record an x402 Transaction After the x402 payment executes onchain: ```bash curl -X POST https://conto.finance/api/sdk/x402/record \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 0.05, "recipientAddress": "0xFacilitatorAddress", "transactionHash": "0xabc123...", "resourceUrl": "https://api.example.com/data", "serviceDomain": "api.example.com", "walletId": "WALLET_ID", "chainId": "8453" }' ``` --- ### Check x402 Budget ```bash curl https://conto.finance/api/sdk/x402/budget \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` --- ### Approve and Confirm an External Wallet Payment Use this flow when your agent holds its own keys and Conto should only authorize the spend. ```bash # Step 1: Ask Conto for approval APPROVAL=$(curl -s -X POST https://conto.finance/api/sdk/payments/approve \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 50, "recipientAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18", "senderAddress": "0x1a2b3c4d5e6f...", "purpose": "Vendor payout", "chainId": 42431 }') echo $APPROVAL # Step 2: Agent executes the transfer with its own wallet tools # Step 3: Confirm the onchain transfer back to Conto curl -X POST https://conto.finance/api/sdk/payments/APPROVAL_ID/confirm \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "txHash": "0xabc123...", "approvalToken": "APPROVAL_TOKEN" }' ``` --- ## Policies ### Limit Agent to $50/Day ```bash curl -X POST https://conto.finance/api/policies \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Daily Cap: $50", "policyType": "SPEND_LIMIT", "rules": [ { "ruleType": "DAILY_LIMIT", "operator": "LTE", "value": "50", "action": "ALLOW" } ] }' ``` Then assign to agent: ```bash curl -X POST https://conto.finance/api/agents/AGENT_ID/policies \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "policyId": "POLICY_ID" }' ``` --- ### Require Approval Above $100 ```bash curl -X POST https://conto.finance/api/policies \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Approval Above $100", "policyType": "APPROVAL_THRESHOLD", "rules": [ { "ruleType": "REQUIRE_APPROVAL_ABOVE", "operator": "GREATER_THAN", "value": "100", "action": "REQUIRE_APPROVAL" } ] }' ``` --- ### Block Payments Outside Business Hours ```bash curl -X POST https://conto.finance/api/policies \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Business Hours Only", "policyType": "TIME_RESTRICTION", "rules": [ { "ruleType": "TIME_WINDOW", "operator": "BETWEEN", "value": "09:00-17:00", "timezone": "America/New_York", "action": "ALLOW" }, { "ruleType": "DAY_OF_WEEK", "operator": "IN", "value": "MON,TUE,WED,THU,FRI", "action": "ALLOW" } ] }' ``` --- ### Allowlist Specific Recipients ```bash curl -X POST https://conto.finance/api/policies \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Approved Vendors Only", "policyType": "COUNTERPARTY", "rules": [ { "ruleType": "ALLOWED_COUNTERPARTIES", "value": "0xVendorA,0xVendorB,0xVendorC", "action": "ALLOW" } ] }' ``` --- ### Cap x402 API Spending ```bash curl -X POST https://conto.finance/api/policies \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "x402 Guardrails", "policyType": "X402_CONTROLS", "rules": [ { "ruleType": "X402_MAX_PER_REQUEST", "operator": "LTE", "value": "0.10", "action": "ALLOW" }, { "ruleType": "X402_MAX_PER_SERVICE", "operator": "LTE", "value": "25", "action": "ALLOW" } ] }' ``` --- ### Restrict to Allowed x402 Services ```bash curl -X POST https://conto.finance/api/policies \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "x402 Service Allowlist", "policyType": "X402_CONTROLS", "rules": [ { "ruleType": "X402_ALLOWED_SERVICES", "value": "api.example.com,data.provider.io", "action": "ALLOW" } ] }' ``` --- ### Cap MPP Session Deposits ```bash curl -X POST https://conto.finance/api/policies \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "MPP Session Guardrails", "policyType": "MPP_CONTROLS", "rules": [ { "ruleType": "MPP_SESSION_BUDGET", "operator": "LTE", "value": "25", "action": "ALLOW" }, { "ruleType": "MPP_MAX_PER_REQUEST", "operator": "LTE", "value": "1.00", "action": "ALLOW" }, { "ruleType": "MPP_MAX_CONCURRENT_SESSIONS", "operator": "LTE", "value": "3", "action": "ALLOW" } ] }' ``` --- ### Restrict MPP to Allowed Services ```bash curl -X POST https://conto.finance/api/policies \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "MPP Service Allowlist", "policyType": "MPP_CONTROLS", "rules": [ { "ruleType": "MPP_ALLOWED_SERVICES", "value": "api.service.com,streaming.provider.io", "action": "ALLOW" } ] }' ``` --- ## Approvals & Trust ### Look Up Trust for a Counterparty Address ```bash curl https://conto.finance/api/sdk/network/trust/0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18 \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` Requires the `network:read` scope. Returns global trust information, relationship-specific trust data, transaction history, and any network flags. --- ### Require Approval for Large Payments and Use Slack for Review The fastest production pattern is: 1. Create an approval threshold rule: ```bash curl -X POST https://conto.finance/api/policies \ -H "Authorization: Bearer $CONTO_ORG_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Approval Above $500", "policyType": "APPROVAL_THRESHOLD", "rules": [ { "ruleType": "REQUIRE_APPROVAL_ABOVE", "operator": "GREATER_THAN", "value": "500", "action": "REQUIRE_APPROVAL" } ] }' ``` 2. Configure a Slack approval channel: See [/guides/external-approvals](https://conto.finance/docs/guides/external-approvals) for the full Slack setup and approval action flow. --- ## MPP Payments ### Pre-Authorize an MPP Session ```bash curl -X POST https://conto.finance/api/sdk/mpp/pre-authorize \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 10.00, "recipientAddress": "0xServiceAddress", "resourceUrl": "https://api.service.com/stream", "serviceDomain": "api.service.com", "intent": "session", "depositAmount": 10.00 }' ``` Returns `"authorized": true` with wallet details, or `"authorized": false` with violation reasons. --- ### Record an MPP Settlement After closing a session, record the settled amount: ```bash curl -X POST https://conto.finance/api/sdk/mpp/record \ -H "Authorization: Bearer $CONTO_SDK_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 3.50, "recipientAddress": "0xServiceAddress", "transactionHash": "0xsettlement123...", "resourceUrl": "https://api.service.com/stream", "serviceDomain": "api.service.com", "sessionId": "mpp_session_xyz", "scheme": "mpp", "walletId": "WALLET_ID", "chainId": "4217" }' ``` Record the **settled amount**, not the deposit amount. --- ### Check MPP Budget ```bash curl https://conto.finance/api/sdk/mpp/budget \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` --- ### List MPP Services Used ```bash curl https://conto.finance/api/sdk/mpp/services \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` --- ## Monitoring ### Get Agent Spending Summary ```bash curl https://conto.finance/api/sdk/analytics/spend \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` --- ### Get Wallet Balance ```bash curl https://conto.finance/api/sdk/wallets \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` Returns all linked wallets with current balances. --- ### List Active Alerts ```bash curl https://conto.finance/api/alerts \ -H "Authorization: Bearer $CONTO_ORG_KEY" ``` --- ## TypeScript SDK Equivalents The recipes above use curl. Here are the payment operations in TypeScript using the SDK: ```typescript import { Conto } from '@conto/sdk'; const conto = new Conto({ apiKey: process.env.CONTO_SDK_KEY! }); // Request + execute payment const req = await conto.payments.request({ amount: 10, recipientAddress: '0x...', purpose: 'Service payment', }); if (req.status === 'APPROVED') { const tx = await conto.payments.execute(req.requestId); console.log(tx.explorerUrl); } // Check status const status = await conto.payments.status(req.requestId); // Single-call payment (request + execute) const result = await conto.payments.pay({ amount: 50, recipientAddress: '0x...', purpose: 'API credits', }); console.log(result.txHash); ``` Note: The `Conto` class provides `conto.payments` for agent payment operations. For admin operations (agents, wallets, policies) use `ContoAdmin` — see the [Admin SDK](https://conto.finance/docs/sdk/admin). For x402, MPP, transaction listing, and wallet queries, use the REST API endpoints shown in the curl recipes above. ## Related ### Choose Integration Link: https://conto.finance/docs/guides/choose-your-integration SDK vs OpenClaw vs Hermes vs x402 vs MPP ### First Payment Link: https://conto.finance/docs/guides/first-agent-payment Full setup walkthrough ### x402 Payments Link: https://conto.finance/docs/guides/x402-api-payments Pay for APIs with x402 ### MPP Sessions Link: https://conto.finance/docs/guides/mpp-session-payments Session-based micropayments ### Approval Workflows Link: https://conto.finance/docs/guides/approval-workflows Add review and escalation controls ### Trust Scoring Link: https://conto.finance/docs/guides/trust-scoring Use counterparty trust as a control surface --- # Roles & Permissions Conto uses role-based access control (RBAC) to manage what each team member can do within an organization. ## Roles | Role | Description | |------|-------------| | **Owner** | Full access. Can manage billing, settings, encryption keys, and all resources. One per organization. | | **Admin** | Can manage agents, wallets, policies, API keys, and team members. Cannot change billing or encryption. | | **Manager** | Can create and update agents, manage counterparties, approve transactions, and manage alerts. Cannot create wallets or policies. | | **Viewer** | Read-only access to agents, wallets, policies, transactions, counterparties, alerts, and analytics. | | **Member** | Limited access. Can view assigned resources but cannot modify anything. | ## Permission Matrix | Permission | Owner | Admin | Manager | Viewer | Member | |-----------|-------|-------|---------|--------|--------| | **Agents** | | | | | | | View agents | Yes | Yes | Yes | Yes | -- | | Create agents | Yes | Yes | Yes | -- | -- | | Update agents | Yes | Yes | Yes | -- | -- | | Delete agents | Yes | Yes | -- | -- | -- | | Suspend/freeze agents | Yes | Yes | Yes | -- | -- | | **Wallets** | | | | | | | View wallets | Yes | Yes | Yes | Yes | -- | | Create wallets | Yes | Yes | -- | -- | -- | | Fund wallets | Yes | Yes | -- | -- | -- | | Withdraw from wallets | Yes | -- | -- | -- | -- | | **Policies** | | | | | | | View policies | Yes | Yes | Yes | Yes | -- | | Create/edit policies | Yes | Yes | -- | -- | -- | | **Transactions** | | | | | | | View transactions | Yes | Yes | Yes | Yes | -- | | Execute transactions | Yes | Yes | Yes | -- | -- | | Approve transactions | Yes | Yes | -- | -- | -- | | **Counterparties** | | | | | | | View counterparties | Yes | Yes | Yes | Yes | -- | | Manage counterparties | Yes | Yes | Yes | -- | -- | | **Alerts** | | | | | | | View alerts | Yes | Yes | Yes | Yes | -- | | Acknowledge/resolve | Yes | Yes | Yes | -- | -- | | **Analytics** | | | | | | | View analytics | Yes | Yes | Yes | Yes | -- | | Export data | Yes | Yes | -- | -- | -- | | **Settings** | | | | | | | View settings | Yes | Yes | -- | -- | -- | | Modify settings | Yes | -- | -- | -- | -- | | **Team** | | | | | | | View members | Yes | Yes | Yes | Yes | -- | | Invite/remove members | Yes | Yes | -- | -- | -- | | **API Keys** | | | | | | | View API keys | Yes | Yes | -- | -- | -- | | Create/revoke API keys | Yes | -- | -- | -- | -- | ## Managing Members ### Invite a Member Go to **Settings** > **Team** > **Invite Member**. Enter the email and select a role. ### Change a Role Only users with a higher role hierarchy can change another member's role. An Admin can change a Manager's role but not another Admin's. Role hierarchy (highest to lowest): Owner > Admin > Manager > Viewer > Member. ## API Key Scopes Organization API keys use scopes that map to these permissions. When creating a key, you can select a preset or pick individual scopes: | Preset | Scopes included | |--------|----------------| | **Read Only** | `*:read` scopes only | | **Standard** | Read + write for agents, wallets, policies, transactions, counterparties | | **Admin** | All scopes (Owner-only) | See [Admin SDK > Scopes](https://conto.finance/docs/sdk/admin#scopes) for the full scope list. --- # Securing Agents with Policies An agent without policies is an open wallet. This guide walks through configuring the policies, alerts, and controls that make Conto valuable in production — from basic spend caps to x402 micropayment budgets. ## Prerequisites An active agent with a linked wallet Familiarity with the payment request/execute flow ([Quickstart: Connect Agent & Wallet](https://conto.finance/docs/quickstart/first-agent)) ## How Policies Work Before diving in, three rules to remember: 1. **AND logic** — All assigned policies are evaluated. The most restrictive outcome wins. 2. **First deny stops** — If any policy returns DENY, evaluation stops immediately. 3. **Wallet limits first** — Wallet-level per-transaction/daily/weekly/monthly limits are checked before policy rules. ``` Payment Request → Wallet Limits (per-tx, daily, weekly, monthly) → Org-Level Policies → Agent-Level Policies → Result: APPROVED / DENIED / REQUIRES_APPROVAL ``` ## Layer 1: Spending Limits The foundation. Every production agent should have spend caps. ### Daily Budget Prevents a runaway agent from draining the wallet in a single day. Go to **Policies** → **New Policy**. | Field | Value | |-------|-------| | Name | Daily Budget: $100 | | Policy Type | SPEND_LIMIT | | Field | Value | |-------|-------| | Rule Type | DAILY_LIMIT | | Operator | LTE | | Value | 100 | | Action | ALLOW | The agent can spend up to $100 per day across all transactions. Resets at midnight UTC. Go to the agent's **Permissions** tab and assign this policy. ### Per-Transaction Cap Prevents any single large transaction, even if the daily budget has room. | Field | Value | |-------|-------| | Rule Type | MAX_AMOUNT | | Operator | LTE | | Value | 25 | | Action | ALLOW | Add this as a second rule in the same policy, or create a separate policy. Both approaches work — Conto evaluates all rules regardless. ### Human Approval for Large Payments Automatically escalate payments above a threshold for human review. | Field | Value | |-------|-------| | Name | Approval Above $50 | | Policy Type | APPROVAL_THRESHOLD | | Rule Type | REQUIRE_APPROVAL_ABOVE | | Operator | GREATER_THAN | | Value | 50 | | Action | REQUIRE_APPROVAL | Payments over $50 show up in **Pending Approvals** in the dashboard. The agent receives `REQUIRES_APPROVAL` and can poll or use webhooks to check status. Info: A common pattern: set MAX_AMOUNT to $200 (hard deny above) and REQUIRE_APPROVAL_ABOVE to $50 (human review in the middle). Payments under $50 flow automatically, $50-$200 need approval, and above $200 are blocked outright. ## Layer 2: Counterparty Controls Control *who* your agent can pay, not just *how much*. ### Allowlist Known Recipients Only permit payments to pre-approved addresses. | Field | Value | |-------|-------| | Name | Approved Recipients Only | | Policy Type | COUNTERPARTY | | Field | Value | |-------|-------| | Rule Type | ALLOWED_COUNTERPARTIES | | Value | Comma-separated list of addresses | | Action | ALLOW | Only addresses in this list can receive payments. All others are denied. ### Block Specific Addresses Alternatively, allow all recipients except specific blocked ones: | Field | Value | |-------|-------| | Rule Type | BLOCKED_COUNTERPARTIES | | Value | `0xSuspiciousAddress1, 0xSuspiciousAddress2` | | Action | DENY | ### Trust Score Threshold Require counterparties to have a minimum trust score before receiving payments: | Field | Value | |-------|-------| | Rule Type | TRUST_SCORE | | Operator | GTE | | Value | 0.5 | | Action | ALLOW | New, unknown counterparties start with a low trust score. As transaction history builds, their score increases. See [Trust Providers](https://conto.finance/docs/integrations/trust-providers) for how scoring works. ## Layer 3: Time Controls Restrict *when* your agent can make payments. ### Business Hours Only ``` Allow payments Monday-Friday, 9am-6pm Eastern ``` | Field | Value | |-------|-------| | Name | Business Hours Only | | Policy Type | TIME_RESTRICTION | | Field | Value | |-------|-------| | Rule Type | TIME_WINDOW | | Start Time | 09:00 | | End Time | 18:00 | | Timezone | America/New_York | | Action | ALLOW | | Field | Value | |-------|-------| | Rule Type | DAY_OF_WEEK | | Value | MON, TUE, WED, THU, FRI | | Action | ALLOW | Both rules must pass (AND logic). Payments outside business hours or on weekends are denied. ### Blackout Periods Block payments during specific date ranges (holidays, maintenance windows): | Field | Value | |-------|-------| | Rule Type | BLACKOUT_PERIOD | | Start Date | 2025-12-24 | | End Date | 2025-12-26 | | Action | DENY | ## Layer 4: x402 Micropayment Controls If your agent pays for APIs using the [x402 protocol](https://conto.finance/docs/sdk/x402-payments), add dedicated micropayment policies. ### Cap Per API Call Prevent a single expensive API call from draining the budget: | Field | Value | |-------|-------| | Rule Type | X402_MAX_PER_REQUEST | | Operator | LTE | | Value | 0.10 | | Action | ALLOW | No single x402 payment can exceed $0.10. ### Budget Per Service Limit total spending on a specific API service: | Field | Value | |-------|-------| | Rule Type | X402_MAX_PER_SERVICE | | Operator | LTE | | Value | 50 | | Action | ALLOW | Total spend per service domain cannot exceed $50. ### Allowlist Approved Services Only allow x402 payments to known API providers: | Field | Value | |-------|-------| | Rule Type | X402_ALLOWED_SERVICES | | Value | `api.example.com, data.provider.io` | | Action | ALLOW | Payments to any other x402 service domain are denied. ## Layer 5: Alerts Policies prevent bad transactions. Alerts tell you when something interesting happens. ### Recommended Alert Configuration Go to **Alerts** in the sidebar and configure: | Alert Type | Threshold | Why | |------------|-----------|-----| | High-Value Transaction | $50+ | Know when large payments execute | | Spend Limit Warning | 80% of daily limit | React before the agent hits its cap | | Low Balance | $50 remaining | Top up before payments start failing | | Policy Violation | Any | Track what the agent is trying to do that gets blocked | | New Counterparty | First transaction | Know when the agent pays someone new | ### x402-Specific Alerts If using x402 micropayments, also enable: | Alert Type | Why | |------------|-----| | Price Spike | API suddenly charging more than usual | | High Frequency | Agent making unusually many API calls | | New Service | Agent paying a service for the first time | | Budget Burn Rate | Approaching x402 budget limits | ## Putting It Together: Example Configuration Here's a production-ready policy stack for a typical AI agent: | Policy | Rules | Effect | |--------|-------|--------| | **Spend Controls** | MAX_AMOUNT ≤ $50, DAILY_LIMIT ≤ $200 | Hard caps on individual and daily spending | | **Human Review** | REQUIRE_APPROVAL_ABOVE $25 | Manager reviews payments $25-$50 | | **Known Recipients** | ALLOWED_COUNTERPARTIES: [list] | Only pays approved addresses | | **Business Hours** | TIME_WINDOW 9-18, DAY_OF_WEEK M-F | No weekend/overnight payments | | **x402 Guardrails** | MAX_PER_REQUEST ≤ $0.10, MAX_PER_SERVICE ≤ $20 | Micropayment safety net | With this stack, the agent can: - Automatically pay up to $25 to known recipients during business hours - Request human approval for $25-$50 payments - Never exceed $50 in a single transaction or $200/day - Pay for x402 APIs up to $0.10/call and $20/service ## Verifying Your Policies After setting up, verify with the setup endpoint: ```bash curl https://conto.finance/api/sdk/setup \ -H "Authorization: Bearer $CONTO_API_KEY" ``` The `policies` array shows all active policies and their rules. Run a few [test transactions](https://conto.finance/docs/guides/testing-payments) to confirm enforcement before going live. ## Troubleshooting Policies must be assigned to the agent in the **Permissions** tab. Creating a policy doesn't apply it automatically. Also verify the policy status is ACTIVE (not DRAFT or DISABLED). Policies stack with AND logic. If you have 5 restrictive policies, the agent must pass all of them. Review each policy's rules and consider loosening thresholds or removing redundant policies. Organization-level policies apply to all agents and take priority. If an org policy caps transactions at $25, an agent-level policy allowing $100 won't override it. Check with your org admin. Wallet-level limits (set when linking a wallet) are checked first and separately from policies. If the wallet per-transaction limit is $10 but your policy allows $50, the wallet limit wins. Edit wallet limits from the agent detail page. ## Next Steps ### Recipes Link: https://conto.finance/docs/guides/recipes Copy-paste policy configurations for common scenarios ### Advanced Policies Link: https://conto.finance/docs/policies/advanced x402, MPP, geographic, and DeFi policy rules ### Trust Providers Link: https://conto.finance/docs/integrations/trust-providers Counterparty trust scoring and verification ### Policy Testing Link: https://conto.finance/docs/guides/policy-testing Step-by-step policy enforcement testing --- # Testing Payments Safely Before going to production, you need confidence that your agent integration handles every scenario — approved, denied, requires approval, insufficient balance, and more. This guide covers how to test each one safely on Tempo Testnet. ## Prerequisites An agent with an SDK key ([Quickstart: Connect Agent & Wallet](https://conto.finance/docs/quickstart/first-agent)) A funded Tempo Testnet wallet linked to your agent ## Why Tempo Testnet | | Tempo Testnet | Production (Tempo / Base / Solana) | |---|---|---| | **Token** | pathUSD (free) | USDC.e on Tempo or USDC on Base / Solana | | **Gas fees** | None | Covered by Conto | | **Onchain** | Yes — real blockchain transactions | Yes | | **Policies** | Fully enforced | Fully enforced | | **Best for** | Integration testing, policy validation | Live agent operations | Tempo Testnet gives you real onchain transactions with real policy enforcement, using free test tokens. Your code stays the same when you switch to production — only the wallet and chain change. ## Test Plan This guide walks through 5 scenarios that cover the core payment lifecycle: | # | Scenario | Expected Result | |---|----------|-----------------| | 1 | Simple payment under limits | APPROVED → execute → confirmed | | 2 | Payment exceeding spend limit | DENIED | | 3 | Payment requiring approval | REQUIRES_APPROVAL | | 4 | Payment to blocked counterparty | DENIED | | 5 | Payment outside time window | DENIED | ## Setup: Create Test Policies Create these policies to test against. If you already have policies from the quickstart, you can reuse or modify them. ### Policy: Spend Cap Go to **Policies** → **New Policy**. | Field | Value | |-------|-------| | Name | Test: Spend Cap | | Policy Type | SPEND_LIMIT | | Field | Value | |-------|-------| | Rule Type | MAX_AMOUNT | | Operator | LTE | | Value | 25 | | Action | ALLOW | Transactions above $25 are denied. ### Policy: Approval Threshold | Field | Value | |-------|-------| | Name | Test: Approval Threshold | | Policy Type | APPROVAL_THRESHOLD | | Field | Value | |-------|-------| | Rule Type | REQUIRE_APPROVAL_ABOVE | | Operator | GREATER_THAN | | Value | 15 | | Action | REQUIRE_APPROVAL | ### Policy: Blocked Counterparty | Field | Value | |-------|-------| | Name | Test: Blocked Address | | Policy Type | COUNTERPARTY | | Field | Value | |-------|-------| | Rule Type | BLOCKED_COUNTERPARTIES | | Value | `0xBLOCKED0000000000000000000000000000dead` | | Action | DENY | Assign all three policies to your test agent in the **Permissions** tab. ## Scenario 1: Approved Payment A $5 payment — under all thresholds. ```bash curl -X POST https://conto.finance/api/sdk/payments/request \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 5, "recipientAddress": "0x1234567890abcdef1234567890abcdef12345678", "purpose": "Test: approved payment", "category": "TESTING" }' ``` **Verify:** - Response `status` is `"APPROVED"` - Execute it and confirm `txHash` is returned - Check **Transactions** in the dashboard — status should be "Confirmed" - Check **Audit Logs** — you should see the policy evaluation trail ```bash # Execute the approved payment curl -X POST https://conto.finance/api/sdk/payments/REQUEST_ID/execute \ -H "Authorization: Bearer $CONTO_API_KEY" ``` ## Scenario 2: Denied by Spend Limit A $30 payment — exceeds the $25 cap. ```bash curl -X POST https://conto.finance/api/sdk/payments/request \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 30, "recipientAddress": "0x1234567890abcdef1234567890abcdef12345678", "purpose": "Test: over spend cap" }' ``` **Verify:** - Response `status` is `"DENIED"` - `violations` array contains a message referencing the spend cap - The `requestId` exists but cannot be executed **Handle it in code:** ```typescript const result = await conto.payments.request({ amount: 30, recipientAddress: '0x1234567890abcdef1234567890abcdef12345678', purpose: 'Test: over spend cap', }); if (result.status === 'DENIED') { console.log('Blocked by policy:', result.violations[0].message); // Your agent should log this and try a smaller amount or escalate } ``` ## Scenario 3: Requires Approval A $20 payment — under the $25 cap but over the $15 approval threshold. ```bash curl -X POST https://conto.finance/api/sdk/payments/request \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 20, "recipientAddress": "0x1234567890abcdef1234567890abcdef12345678", "purpose": "Test: needs approval" }' ``` **Verify:** - Response `status` is `"REQUIRES_APPROVAL"` - The payment appears in **Pending Approvals** in the dashboard - A human can approve or reject it from the dashboard - After approval, the agent can execute it **Handle it in code:** ```typescript const result = await conto.payments.request({ amount: 20, recipientAddress: '0x1234567890abcdef1234567890abcdef12345678', purpose: 'Test: needs approval', }); if (result.status === 'REQUIRES_APPROVAL') { console.log('Waiting for human approval:', result.requestId); // Poll for status change, or use webhooks to get notified } ``` ## Scenario 4: Blocked Counterparty A $5 payment to a blocked address. ```bash curl -X POST https://conto.finance/api/sdk/payments/request \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 5, "recipientAddress": "0xBLOCKED0000000000000000000000000000dead", "purpose": "Test: blocked recipient" }' ``` **Verify:** - Response `status` is `"DENIED"` - `violations` reference the blocked counterparty policy - Amount doesn't matter — the address itself is blocked ## Scenario 5: Time Window Restriction To test time-based restrictions, temporarily add a time window policy: | Field | Value | |-------|-------| | Name | Test: Business Hours Only | | Policy Type | TIME_RESTRICTION | | Rule Type | TIME_WINDOW | | Start Time | 09:00 | | End Time | 17:00 | | Timezone | Your timezone | | Action | ALLOW | Add it in the **Permissions** tab. If it's currently outside 9am-5pm in your timezone, any payment request will be denied with a time window violation. If it's during business hours, you can temporarily set the window to a past range (e.g., 02:00-03:00) to trigger the denial. Remove the time window policy after testing to avoid blocking your other tests. ## Checking Results in Bulk After running all scenarios, review everything in one place: ### Transactions Page Go to **Transactions** in the dashboard. You should see: - Confirmed transactions from Scenario 1 - Denied/rejected entries from Scenarios 2, 4, 5 - A pending-approval entry from Scenario 3 ### Audit Logs Go to **Audit Logs** to see the policy evaluation trail for every request. Each entry shows: - Which policies were evaluated - Which rules matched - The final decision and reasoning ### Via API ```bash # List recent transactions curl https://conto.finance/api/sdk/transactions \ -H "Authorization: Bearer $CONTO_API_KEY" ``` ## Testing x402 Micropayments If your agent uses x402 protocol payments (paying for APIs), test the pre-authorize → record flow: ```bash # Pre-authorize an x402 payment curl -X POST https://conto.finance/api/sdk/x402/pre-authorize \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 0.05, "recipientAddress": "0x1234567890abcdef1234567890abcdef12345678", "resourceUrl": "https://api.example.com/data", "serviceDomain": "api.example.com" }' ``` If authorized, the response includes `"authorized": true` and the selected wallet. ## Moving to Production Once all 5 scenarios pass: 1. Create a production wallet on **Base** (USDC) or **Solana** (USDC) 2. Fund it with real stablecoins 3. Link it to your agent with production-appropriate limits 4. Adjust policies for production thresholds (the test policies can remain as-is) 5. Your integration code stays the same — no code changes needed Info: You can keep your testnet wallet and policies active alongside production. Many teams run test agents permanently for regression testing. ## Troubleshooting Check the **Permissions** tab on the agent detail page. Policies must be explicitly assigned to the agent. Creating a policy doesn't automatically apply it. Policies use AND logic — the most restrictive outcome wins. If one policy denies and another requires approval, denial takes priority. Check which policies are assigned and their rule values. Double-check the timezone setting on the policy. If your local timezone doesn't match the policy timezone, the enforcement window may be offset. The wallet balance was sufficient at request time but dropped before execution. This can happen if other transactions executed between request and execute. Re-fund the testnet wallet. ## Next Steps ### Secure Your Agent Link: https://conto.finance/docs/guides/securing-agents Production-ready policy configurations ### Recipes Link: https://conto.finance/docs/guides/recipes Copy-paste solutions for specific tasks ### Error Handling Link: https://conto.finance/docs/sdk/error-handling Handle every error code gracefully ### Policy Reference Link: https://conto.finance/docs/policies/overview All policy types and rule operators --- # Trust Scoring and Counterparty Controls Trust scoring is how Conto turns recipient history, verification, and network intelligence into a practical control surface for agent payments. It helps answer a simple question before money moves: > How comfortable should this agent be paying this address right now? ## Two Layers of Trust Conto maintains two complementary trust scoring layers: - **Per-organization trust** — your org's local view of a counterparty based on your own transaction history with them. - **Network trust** — a cross-organization view of a counterparty across all Conto customers, treating signals like a credit bureau. Policies can use either layer. The per-org score is most relevant for repeat counterparties; the network score is most useful for cold-start decisions on addresses you have not transacted with before. ## What Feeds the Trust Score Conto calculates trust with four weighted components: | Component | Weight | What it reflects | | ---------------- | ------ | ---------------------------------------------------------- | | **History** | `30%` | Transaction count, volume, and relationship depth | | **Reliability** | `30%` | Success rate, failed transactions, flagged activity | | **Activity** | `20%` | Account age, recency, and consistency of behavior | | **Verification** | `20%` | Manual verification, network data, and external enrichment | This means trust is not based on a single signal. A new counterparty can improve over time through successful activity, while a once-safe counterparty can degrade if failure or alert signals appear. ## How Trust Levels Are Assigned Internally, Conto calculates a score on a `0.0` to `1.0` scale and then maps it to a trust level. | Internal score | Trust level | Notes | | -------------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `< 0.2` | `UNKNOWN` or `BLOCKED` | Counterparties with low scores only become `BLOCKED` when there are actual negative signals, such as failed or flagged transactions | | `0.2 - 0.49` | `UNKNOWN` | Limited history or weak signal quality | | `0.5 - 0.74` | `VERIFIED` | Reasonable confidence, but not yet a fully trusted recipient | | `>= 0.75` | `TRUSTED` | High-confidence recipient, assuming verification requirements are satisfied | Note: Low data is not the same as malicious behavior. Conto treats many new counterparties as `UNKNOWN` rather than auto-blocking them. ## External Enrichment and Compliance Signals ### Conto Network Intelligence Conto aggregates anonymized network signals across organizations to improve counterparty evaluation. This data takes precedence when it exists because it reflects real payment behavior on the platform. ### Fairscale for Solana For Solana addresses with no existing network trust score, Conto can enrich the counterparty with Fairscale data. - Used for cold-start reputation on Solana - Normalized into Conto's `0.0 - 1.0` trust scale - Fail-open design: enrichment helps, but provider unavailability does not automatically block ### Sanctions Screening Sanctions and compliance checks are separate from trust scoring. - Local OFAC screening is built in - Chainalysis and TRM Labs can be layered on for enterprise use - Compliance checks are fail-closed for enterprise screening providers That means a counterparty can be high-trust from a behavior standpoint and still be blocked for compliance reasons. ## How Trust Becomes a Control Trust data feeds directly into policy evaluation. Common trust-aware controls include: - `TRUST_SCORE` thresholds - Counterparty status and trust-level rules - New-recipient approval workflows - Auto-freeze and alert thresholds when trust drops sharply For more on provider inputs, see [/integrations/trust-providers](https://conto.finance/docs/integrations/trust-providers). For policy rule details, see [/policies/counterparties](https://conto.finance/docs/policies/counterparties). ## Canonical Patterns ### 1. New vendor safe path - Unknown counterparties are allowed to exist but not to spend freely. - Require approval for first payments or low-trust recipients. - Promote to normal flow once transaction history and verification improve. ### 2. Trusted vendor fast path - Allow trusted or verified counterparties to pass with fewer interruptions. - Keep hard denies for sanctioned or blocked recipients. - Layer spend limits and time windows on top of trust so no single signal has too much power. ### 3. Solana cold-start enrichment - Use Fairscale when a Solana recipient has no Conto network history. - Combine that score with a conservative approval threshold. - Let the relationship graduate as real transaction history builds. ### 4. Trust deterioration response - Watch for trust score drops, repeated failures, or new flags. - Route affected counterparties into approval workflows. - Use alerts or auto-freeze when trust degradation becomes severe. ## Look Up Trust Programmatically Use the SDK network trust endpoint to inspect any wallet address: ```bash curl https://conto.finance/api/sdk/network/trust/0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18 \ -H "Authorization: Bearer $CONTO_SDK_KEY" ``` The response includes: - Global trust information - Agent-specific relationship trust, when it exists - Flags and risk indicators - Aggregate transaction history This is a useful building block for preflight checks, ops tooling, or AI assistant workflows. ## Trust, Approvals, and Policy Design Trust scoring works best as part of a layered model: 1. Use trust to classify recipients. 2. Use approvals to review the gray area. 3. Use hard policy denies for clearly forbidden destinations. That gives you three lanes instead of one: - **Fast lane** for trusted counterparties - **Review lane** for unknown or changing counterparties - **Blocked lane** for clearly disallowed behavior ## Related Guides ### Trust Providers Link: https://conto.finance/docs/integrations/trust-providers See Fairscale, sanctions screening, and provider priority ### Approval Workflows Link: https://conto.finance/docs/guides/approval-workflows Use trust levels as a review trigger ### Securing Agents Link: https://conto.finance/docs/guides/securing-agents Build a layered risk model for agents that spend ### Recipes Link: https://conto.finance/docs/guides/recipes Copy-paste trust lookup and policy setup commands --- # Webhooks Conto sends HTTP POST requests to your configured URL when events occur — payments requested, approved, denied, executed, confirmed, or failed. ## Setup 1. Go to **Settings** > **Webhooks** in the dashboard 2. Enter your HTTPS endpoint URL 3. Copy the generated **signing secret** — you'll need this to verify payloads Webhook URLs must use HTTPS. HTTP, localhost, and private IP ranges are blocked for security (SSRF protection). ## Event Types | Event | Fires when | |-------|-----------| | `payment.requested` | A payment request is created | | `payment.approved` | A payment passes policy evaluation | | `payment.denied` | A payment is blocked by policy | | `payment.executed` | A payment transaction is submitted onchain | | `payment.confirmed` | A payment is confirmed onchain | | `payment.failed` | A payment transaction fails | | `service_payment.recorded` | An x402 or MPP transaction is recorded | | `service_payment.failed` | An x402 or MPP transaction fails | | `agent.created` | A new agent is registered | | `agent.updated` | An agent's status or config changes | | `agent.paused` | An agent is paused | | `agent.resumed` | An agent is resumed from paused state | | `wallet.funded` | A wallet receives funds | | `wallet.low_balance` | A wallet balance drops below threshold | | `alert.created` | A new alert is generated | | `policy.violated` | A policy violation is detected | ## Payload Format Every webhook POST includes these headers and a JSON body: ### Headers | Header | Description | |--------|-------------| | `X-Conto-Event` | Event type (e.g., `payment.confirmed`) | | `X-Conto-Timestamp` | ISO 8601 timestamp of the event | | `X-Conto-Signature` | HMAC-SHA256 signature of the body | | `X-Conto-Delivery-Attempt` | Attempt number (1, 2, or 3) | ### Body ```json { "event": "payment.confirmed", "timestamp": "2026-04-07T12:00:00.000Z", "data": { "paymentId": "req_abc123", "amount": 50, "transactionHash": "0xabc...", "transactionId": "tx_xyz", "organizationId": "org_123", "agentId": "agent_456" } } ``` ## Verifying Signatures Every payload is signed with your webhook secret using HMAC-SHA256. Verify signatures to confirm the request came from Conto. ```typescript import { createHmac, timingSafeEqual } from 'crypto'; function verifyWebhook(body: string, signature: string, secret: string): boolean { const expected = createHmac('sha256', secret).update(body).digest('hex'); return timingSafeEqual(Buffer.from(signature), Buffer.from(expected)); } // In your handler app.post('/webhooks/conto', (req, res) => { const signature = req.headers['x-conto-signature']; const isValid = verifyWebhook(JSON.stringify(req.body), signature, process.env.WEBHOOK_SECRET); if (!isValid) { return res.status(401).send('Invalid signature'); } const { event, data } = req.body; switch (event) { case 'payment.confirmed': console.log('Payment confirmed:', data.transactionHash); break; case 'payment.denied': console.log('Payment denied:', data.reasons); break; } res.status(200).send('OK'); }); ``` ## Retry Behavior Failed deliveries are retried up to **3 times** with exponential backoff: | Attempt | Delay | |---------|-------| | 1 | Immediate | | 2 | ~1 second | | 3 | ~4 seconds | A delivery is considered failed if your endpoint returns a non-2xx status code or doesn't respond within **10 seconds**. ## Agent Callback URLs In addition to organization-level webhooks, individual agents can have a `callbackUrl` set during creation. When set, webhooks are delivered to both the organization URL and the agent's callback URL. ## Delivery Targets | Target | Configured via | Receives events for | |--------|---------------|-------------------| | Organization webhook | Settings > Webhooks | All events in the org | | Agent callback URL | Agent config | Events for that agent only | Both targets receive the same payload format and signature headers. --- # Paying for APIs with x402 When your AI agent calls a paid API, the API returns HTTP `402 Payment Required` with a payment challenge. The [x402 protocol](https://www.x402.org/) standardizes this flow — and Conto sits in the middle to enforce policies before the agent pays. This guide walks through the complete cycle: receive a 402, check it with Conto, pay, and track spending. ## Prerequisites An active agent with an SDK key ([Quickstart: Connect Agent & Wallet](https://conto.finance/docs/quickstart/first-agent)) A funded wallet (Base `USDC` or Tempo `USDC.e` for production, or Tempo Testnet `pathUSD` for testing) An x402-enabled API to call (or use the examples below to simulate) ## How x402 Works ``` Agent calls API → HTTP 402 → Conto pre-authorizes → Agent pays & retries → Conto records ``` | Step | Who | What happens | |------|-----|-------------| | 1 | Agent | Calls an x402-enabled API | | 2 | API | Returns `402 Payment Required` with amount, recipient, facilitator | | 3 | Agent | Sends payment details to Conto for policy check | | 4 | Conto | Evaluates x402 policies — returns authorized or denied | | 5 | Agent | If authorized, signs payment and retries the API call | | 6 | Agent | Records the completed transaction in Conto for tracking | Conto never touches the x402 payment itself — it acts as the policy and tracking layer. Your agent (or wallet provider like Sponge) handles the actual signing and payment. ## Step 1: Set Up x402 Policies Before your agent starts paying for APIs, set guardrails. Without x402-specific policies, only your general spend limits apply. ### Recommended Starter Policy Go to **Policies** → **New Policy**. | Field | Value | |-------|-------| | Name | x402 API Guardrails | | Policy Type | X402_CONTROLS | Add these rules to cap per-request cost and total per-service spend: **Rule 1: Cap per API call** | Field | Value | |-------|-------| | Rule Type | X402_MAX_PER_REQUEST | | Operator | LTE | | Value | 0.50 | | Action | ALLOW | No single x402 payment can exceed $0.50. **Rule 2: Budget per service** | Field | Value | |-------|-------| | Rule Type | X402_MAX_PER_SERVICE | | Operator | LTE | | Value | 50 | | Action | ALLOW | Total spend per API service domain cannot exceed $50. Go to the agent's **Permissions** tab and assign the policy. Info: You can also add `X402_ALLOWED_SERVICES` to restrict which API domains your agent can pay. This is the strongest guardrail — the agent can only pay APIs you've explicitly approved. ## Step 2: Pre-Authorize a Payment When your agent receives an HTTP 402, extract the payment details and check them against Conto: ```bash curl -X POST https://conto.finance/api/sdk/x402/pre-authorize \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 0.05, "recipientAddress": "0xFacilitatorAddress", "resourceUrl": "https://api.example.com/data", "serviceDomain": "api.example.com", "facilitator": "0xFacilitatorAddress", "scheme": "exact" }' ``` ### If Authorized ```json { "authorized": true, "wallet": { "id": "wal_123", "address": "0xAgentWallet", "chainId": "8453", "availableBalance": 500.00 }, "reasons": ["Within x402 service budget", "Service domain allowed"] } ``` The agent proceeds to sign the payment and retry the API call. ### If Denied ```json { "authorized": false, "reasons": ["X402 price ceiling exceeded: $0.05 > $0.01 max"], "violations": [ { "type": "X402_PRICE_CEILING", "limit": 0.01, "current": 0.05, "message": "Amount exceeds x402 price ceiling" } ] } ``` The agent should log the denial and either skip the API call or escalate. ## Step 3: Record the Transaction After the x402 payment executes onchain, record it in Conto so it counts toward budgets and shows up in analytics: ```bash curl -X POST https://conto.finance/api/sdk/x402/record \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 0.05, "recipientAddress": "0xFacilitatorAddress", "transactionHash": "0xabc123...", "resourceUrl": "https://api.example.com/data", "serviceDomain": "api.example.com", "facilitator": "0xFacilitatorAddress", "scheme": "exact", "walletId": "wal_123", "chainId": "8453" }' ``` If you skip recording, Conto can't track budget consumption. Your per-service limits won't enforce correctly because Conto doesn't know the payment happened. ### Batch Recording For high-frequency API calls, batch multiple records in one request: ```bash curl -X POST https://conto.finance/api/sdk/x402/record \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "batch": [ { "amount": 0.01, "recipientAddress": "0xFacilitator", "transactionHash": "0x1...", "resourceUrl": "https://api.example.com/v1/data", "serviceDomain": "api.example.com" }, { "amount": 0.02, "recipientAddress": "0xFacilitator", "transactionHash": "0x2...", "resourceUrl": "https://api.example.com/v1/search", "serviceDomain": "api.example.com" } ] }' ``` ## Step 4: Monitor Spending ### Check Budget See how much your agent has spent and what's remaining: ```bash curl https://conto.finance/api/sdk/x402/budget \ -H "Authorization: Bearer $CONTO_API_KEY" ``` ### View Services Used List all x402 API services your agent has interacted with: ```bash curl https://conto.finance/api/sdk/x402/services \ -H "Authorization: Bearer $CONTO_API_KEY" ``` ### Dashboard Go to **Analytics** in the dashboard to see: - x402 spend trends over time - Per-service cost breakdown - Request frequency and average cost per call ## Full Integration Example Here's how an agent handles the complete x402 flow in TypeScript: ```typescript import { Conto } from '@conto/sdk'; const conto = new Conto({ apiKey: process.env.CONTO_API_KEY }); async function callPaidApi(url: string) { // Step 1: Call the API const response = await fetch(url); if (response.status !== 402) { return response.json(); // No payment needed } // Step 2: Parse the 402 payment challenge const paymentHeader = response.headers.get('X-Payment'); const { amount, recipient, facilitator } = JSON.parse(paymentHeader); const serviceDomain = new URL(url).hostname; // Step 3: Pre-authorize through Conto const auth = await conto.x402.preAuthorize({ amount, recipientAddress: recipient, resourceUrl: url, serviceDomain, facilitator, scheme: 'exact', }); if (!auth.authorized) { console.error('Policy denied x402 payment:', auth.reasons); throw new Error(`x402 denied: ${auth.reasons.join(', ')}`); } // Step 4: Sign and pay (via your wallet provider) const { txHash } = await walletProvider.signX402Payment({ amount, recipient, facilitator, }); // Step 5: Retry the API call with payment proof const paidResponse = await fetch(url, { headers: { 'X-Payment-Signature': txHash }, }); // Step 6: Record in Conto await conto.x402.record({ amount, recipientAddress: recipient, transactionHash: txHash, resourceUrl: url, serviceDomain, facilitator, scheme: 'exact', walletId: auth.wallet.id, chainId: auth.wallet.chainId, }); return paidResponse.json(); } ``` ## x402 Policy Reference All available x402-specific policy rules: | Rule Type | What It Controls | |-----------|-----------------| | `X402_MAX_PER_REQUEST` | Max amount per single API call | | `X402_PRICE_CEILING` | Hard ceiling on any x402 payment | | `X402_MAX_PER_ENDPOINT` | Budget per specific API endpoint | | `X402_MAX_PER_SERVICE` | Budget per service domain | | `X402_ALLOWED_SERVICES` | Allowlist of API domains | | `X402_BLOCKED_SERVICES` | Blocklist of API domains | | `X402_ALLOWED_FACILITATORS` | Allowed facilitator addresses | | `X402_VELOCITY_PER_ENDPOINT` | Rate limit per endpoint | | `X402_SESSION_BUDGET` | Session-level spending cap | See [Advanced Policies](https://conto.finance/docs/policies/advanced) for value formats and operators. ## Anomaly Detection Conto automatically monitors x402 patterns and creates alerts for: | Alert | Trigger | |-------|---------| | **Price Spike** | API suddenly charging more than usual | | **High Frequency** | Unusual burst of API calls | | **New Service** | Agent paying an API for the first time | | **Budget Burn** | Approaching per-service budget limit | | **Duplicate Payment** | Same amount + endpoint in quick succession | | **Failed Streak** | Repeated authorization failures | Configure alert thresholds in **Alerts** in the dashboard. ## Troubleshooting Your general spend limits still apply to x402 payments. If the x402 amount plus today's spend exceeds your daily limit, it's denied. Check your wallet-level and agent-level spend limits. Ensure you're recording transactions after they execute. If you skip the record step, Conto can't decrement the budget correctly. Also check if batch records are being sent for high-frequency calls. The allowlist is strict — only listed domains can receive x402 payments. Make sure you've added the exact domain (e.g., `api.example.com`, not `example.com` or `www.api.example.com`). Pre-authorization is a policy check, not an onchain operation. If it's slow, it's likely network latency to the Conto API. Consider caching authorization results for repeated calls to the same endpoint within a short window. ## Next Steps ### MPP Sessions Link: https://conto.finance/docs/guides/mpp-session-payments Session-based micropayments for streaming APIs ### x402 SDK Reference Link: https://conto.finance/docs/sdk/x402-payments Full API reference for x402 endpoints ### Advanced Policies Link: https://conto.finance/docs/policies/advanced All x402 policy rule types and value formats ### Recipes Link: https://conto.finance/docs/guides/recipes Copy-paste x402 recipes --- ## Blog Posts (Full Content) --- Agentic payments have arrived faster than most infrastructure is prepared for. Agents are purchasing services, paying for APIs, buying compute, and executing transactions without humans in the loop. But the current state of agentic commerce resembles something far less structured than a modern financial system. There are no controls for agents that pay. It's the Wild West. ## The New Gunslingers: Agents with Wallets A new class of economic participant is emerging: autonomous agents with the ability to spend money. As intelligence improves, financial autonomy becomes inevitable. An agent that can decide what to do will need the ability to decide what to pay for. It's the next step towards AGI. When you add money into the equation, the significance of every transaction goes up. Powered by stablecoins and programmable payment rails, agents can move money instantly, around the world, at high frequency, all without knowing their counterparties or using intermediaries. This is powerful, but also comes with risks. There are few standardized controls, limited visibility, and almost no purpose-built governance systems for non-human spenders. This leads to: - No clear spending boundaries - Minimal verification of counterparties - Fragmented compliance - Little real-time oversight While stablecoins remove friction, they also lack safeguards embedded in traditional financial systems. The result is that agents can fire off payments as they see fit. If agentic payments is going to be adopted by the enterprise, new controls are needed. ## Why Enterprises Need Guardrails Enterprises operate under strict requirements. There are financial controls they need to adhere to, regulations that are non-negotiable, governance and audit demands, and an overall need to manage risk and reporting. An enterprise deploying thousands of agents faces new challenges including: - Overspending across agents - Payments to malicious counterparties - Agents going rogue - Compliance violations Without a control layer, scaling agentic payments is not viable. Enterprises need a system that sits between agent intent and payment execution, a layer that determines: - Should this agent be allowed to spend? And how much? - Who can it transact with? - Under what conditions? - How is activity monitored and audited? ## What Guardrails Look Like To move from experimentation to production, agentic payments need programmable controls across several areas including: ### 1. Policy and Authorization - Dynamic spending limits - Context-aware approvals - Counterparty allowlists/blocklists - Multi-step authorization for high-risk actions ### 2. Real-Time Observability - Live transaction monitoring - Agent-level spend visibility - Alerts for anomalous behavior ### 3. Compliance and Audit - Immutable transaction logs - Explainability of agent actions - Audit-ready reporting ### 4. Identity and Permissions - Verified agent identities - Scoped permissions by task - Human in the loop approval workflows ### 5. Counterparty Intelligence - Trust scoring for vendors and APIs - Shared risk signals - Detection of malicious actors These are core features needed to take agentic payments from an experiment to enterprise-ready. ## A Control Center for Agentic Payments The existing corporate finance stack was built for humans. Approval workflows, expense reports, procurement systems, and static card limits weren't built for agentic speed and scale. Enterprises need a control center to monitor, govern, and manage agents that spend money. We're building that at **Conto**. From spend management to understanding relationships between agents, APIs, and merchants, we're giving organizations a new way to manage agentic payments. ## Wrangling the New Frontier Every new technology goes through a similar cycle. Experimentation, adoption, the emergence of risk, and the controls and institutionalization of processes that follows. Agentic payments are still early - high potential, high risk, minimal structure. The companies that define this category will be those that introduce trust, control, and visibility. --- Want to securely deploy agentic payments in your organization? Get in touch to see how we can help you manage your agentic spending. [Contact us](mailto:support@conto.finance). --- Agentic payments has taken the timeline by storm (again). But this time they're moving from novelty to infrastructure. Soon agents will purchase services, manage supply chains, procure compute, payout contractors and execute transactions across the internet that previously had humans in the loop. Betting against agentic payments is betting against AGI. It's inevitable. Much of the discussion around agentic payments has focused on consumer use cases - personal AI assistants booking travel, agents shopping online, or bots interacting with marketplaces. The largest opportunity for agentic payments and stablecoins in particular is not consumer commerce - it's the enterprise. Across fintech, retail, e-commerce, logistics, and infrastructure, companies are already deploying AI agents to automate operational workflows, develop new products, and improve the customer experience. As those agents become more capable, they will inevitably take responsibility for payments as well. Industries that demand fast settlement and high-volume programmable transactions are where we'll see adoption first. Giving agents the ability to pay is one step closer to true autonomy because they're putting money on the line. This shift demands a new category of financial infrastructure designed not for humans, but for agents. ## Applications in the Enterprise Enterprises will adopt agentic payments at much larger scale, volume, and value, and with different use cases than consumers. Companies already operate complex workflows that require constant coordination across vendors, suppliers, and infrastructure providers. A few areas where enterprise payment agents are emerging: ### Procurement Agents that monitor pricing and automatically purchase tools, APIs, or services when needed. ### Compute Infrastructure Agents that dynamically purchase compute, storage, or bandwidth from cloud providers or decentralized infrastructure networks. ### Supply Chain Agents that reorder inventory when stock runs low, negotiate vendor pricing, and manage logistics payments. ### Finance and Treasury Agents that manage liquidity, route payments, or optimize settlement rails. Today, humans sit in the middle of those transactions. They click send and fill out the documentation to make sure things happen on time. As agents further integrate into the business, the financial layer will evolve to support agentic payments. Enterprise operations involve massive payment flows. Even small automation improvements can generate significant financial throughput. ## The Payment Chain Era Another significant tailwind driving enterprise adoption of agentic payments is the focus of blockchain infrastructure on enterprise use cases. Previously, blockchain ecosystems focused on retail users, speculative assets, gaming, trading or DeFi. A new generation of payments-focused Layer-1 networks is prioritizing real economic activity. New stablecoin infrastructure like Tempo and Arc is designed specifically to support agentic payments. Programmable, real-time settlement, micropayments, and remittances are just a few of the use cases enabled by this technology. Enabling instant, low cost payments across users, agents, services, and businesses is critical. For enterprises deploying AI agents, this infrastructure removes many of the limitations of legacy financial systems. It is also a perfect fit for the new products that organizations can build with agents. Think about a platform like DoorDash or Deel automating instant contractor payouts with an agent. Or a frontier lab deploying a GPU compute trading agent to secure the best pricing and supply of compute for training runs and to match inference demand. Or fintechs leveraging agents and stablecoin rails to move money faster so it's where their clients want it to be - instead of waiting for wires to go through over the weekend or T+1 or T+2 settlement. ## The Enterprise Problem: Trust Enterprises do not simply need agents that can pay. They need agents they can trust. It's okay to just give your agent a card or wallet and have it spend when you're experimenting with OpenClaw or building an agent over the weekend. Enterprise-grade agentic payments are a whole different beast. Compliance, regulations, governance, making sure the CFO has visibility into agentic spending for the last week - there's a new set of requirements that must be met. Existing corporate financial systems were designed around human employees. Expense platforms like Ramp or Brex manage spending through familiar mechanisms like card limits and approval workflows. These controls work when transactions happen at human speed. They break down when thousands of agents are transacting simultaneously, interacting with counterparties both internal and external to the business. Imagine a company deploying thousands of autonomous agents across its operations. Without proper governance infrastructure, companies face serious risks: - Agents overspending - Interacting with malicious counterparties - Triggering compliance violations - Creating financial exposure across multiple systems Enterprises need spend management, designed from the ground up for agents. Policies, workflows, compliance, the gritty details of what makes companies function needs to apply to agents too. This control layer becomes essential infrastructure for the agent economy. Without it, enterprises cannot safely deploy autonomous financial actors. ## The Agentic Economy The number of AI agents operating across the internet will grow into the trillions. The agent economy will be a new contributor to the GDP of the internet. The enterprise will be a key driver of this transformation - but they cannot capitalize on it without the infrastructure required to govern autonomous financial behavior. 1. **Step 1:** Enabling agents to pay 2. **Step 2:** Building the financial operating system that allows companies to safely deploy these agents Together, they enable the next phase of the internet. Think beyond knowledge agents. It's the age of economic agents. --- Want to securely deploy agentic payments in your organization? Get in touch to see how we can help you manage your agentic spending. [Contact us](mailto:support@conto.finance). --- When an agent is about to send a payment to a Solana wallet it has never interacted with before, what does it know about the recipient? Until now, the answer was: not much. Today we're announcing our integration with [Fairscale](https://fairscale.xyz), a composable reputation scoring system for Solana wallets. Every Solana address that passes through Conto is now automatically enriched with onchain behavioral data. Agents, and the organizations behind them, now have real trust signals before money moves. ## Who Can You Trust? Trust in agentic payments has a cold start problem. When an agent encounters a new counterparty, there's no transaction history to draw from. No past interactions, no success rate, no volume data. The address is a blank slate. For human-driven payments, this might be acceptable. A user can do research, ask questions, or start with a small amount. Agents don't have that luxury. They need to make decisions quickly, often at scale, and they need data to inform those decisions. Without trust data, organizations are left with two options: block unknown addresses entirely (limiting agent capability) or allow them through with no assessment (accepting unquantified risk). Neither option works well. ## What Fairscale Brings ![Fairscale reputation score for a Solana address on the Conto Network page](https://conto.finance/images/blog/fairscale-trust-lookup.png) Fairscale analyzes onchain behavioral signals across Solana to produce a reputation score from 0 to 100. It looks at: - **Transaction patterns** - frequency, consistency, and recency of activity - **Token holdings** - portfolio composition and economic stake - **Staking activity** - participation in network security - **Social connections** - onchain relationships and community involvement This gives us a behavioral fingerprint for any Solana wallet, even if Conto has never seen it before. When a Solana address comes through Conto with no existing network data, Fairscale scores are automatically normalized and used as the trust baseline. Agents and organizations get a real score instead of an empty default. No configuration needed, it's built in for every organization on Conto. ## What This Means for Trust Scoring Fairscale data feeds into Conto's native trust score calculation that comes included as part of our platform. Here's how it fits into the broader picture: 1. **Conto Network Intelligence** comes first - real transaction history from across the platform is the strongest trust signal 2. **Fairscale reputation** fills in the gaps - when an address has no network history on Solana, Fairscale provides a behavioral baseline 3. **Compliance checks** always run regardless of trust score This layered approach means trust scores get more accurate over time. A new Solana address starts with Fairscale data, and as it transacts through Conto, network intelligence takes over with increasingly rich signals. ## Building With the Ecosystem We believe the best infrastructure is composable. The agentic payments and commerce ecosystem is full of teams building specialized, high-quality services. Our job is to bring those tools together in a way that makes it safer and more reliable for agents to transact. As new reputation systems, risk providers, and intelligence platforms emerge across different chains, we will bring the best of them to Conto. Building in the space? [Get in touch](mailto:support@conto.finance)! ## Try It - Soon The Fairscale integration will be included for all organizations on Conto when we go live. Look up any Solana address on the Network page to see its Fairscale reputation data. For organizations that want to enforce reputation thresholds, you can add a Fairscale minimum score policy rule to block payments to wallets below a specified score. --- Want to learn more about how Conto handles agent risk and trust? Send us an email at [support@conto.finance](mailto:support@conto.finance). --- We spent a day at Stripe Sessions. Here's what we learned. Despite the apathy and disillusion on the timeline, agentic commerce is very real. The largest companies in payments, e-commerce, and infrastructure are continuing to invest aggressively and are putting real resources behind it. It was front and center across keynotes and partner sessions. The gap is not conviction. The gap is consumer behavior catching up to the tech. Anyone involved in selling on the internet does not want to be caught off guard the way many were by AI a few years ago. Making products legible to agents, enabling seamless checkout, and laying the right rails for autonomous transactions are all underway. ## Agentic commerce has real backing One of the clearest takeaways from the event was that agentic commerce is not a side conversation anymore. It is becoming part of the roadmap at the companies that already shape how money moves online. The foundations are being set now. Teams want to make sure their products can be discovered, understood, and purchased by agents when that behavior becomes mainstream. That work is happening before consumer behavior fully catches up. ## Stablecoins and MPP are moving into the conversation I was happily surprised by how much airtime MPP, Tempo, Privy, and stablecoins got. Having those topics mentioned by Patrick Collison at the start of the keynote and then demoed by John Collison matters. Stripe is doing a lot to make frontier financial infrastructure understandable and accessible for developers and enterprises. That kind of distribution and validation helps move the whole space forward. The framing felt practical. These technologies are being adopted because they are often the best tools for global, programmable, agent-driven transactions. ## The market is starting to take shape Agentic payments are still early, but the shape of the market is becoming clearer. Stablecoin rails are emerging as the default for new, global, and high-frequency use cases like API access. Enterprise automation is another clear area of interest. Cards still make sense for existing consumer and e-commerce flows. This looks much more like a layering of systems than a zero-sum shift. Tempo and Coinbase both showed strong interest in the category, and their session offered a useful perspective on where things may go next. Giving every Link user the ability to hand an agent a virtual card is incredible distribution. Guardrails, controls, and management of agentic payments came up in almost every conversation. ## Stripe looks well positioned for the agentic economy Stripe's acquisition strategy is elite and has set them up well for this moment. Bridge gives them a leader in stablecoins. Privy gives them a premier digital wallet. Metronome connects directly to the rise of usage-based and AI-driven pricing models. These acquisitions feel thoughtful and aligned with Stripe's long-term strategy, not like short-term acquihires or products that later become afterthoughts. They strengthen the platform and help Stripe stay at the cutting edge of payments. We like to joke about corporate synergies, but Stripe is one of the few companies where they genuinely seem to show up. ## Stripe is shipping with startup speed Stripe's shipping velocity is impressive and still feels a lot like a startup. I think that will be particularly notable when they go public. The number of new products and features showcased, from Treasury to Projects CLI and more, made it clear that Stripe is well positioned to thrive in the AI era. They are not waiting to be disrupted. They are trying to build a company that stays durable for the next several decades. ## The data may have been the strongest signal Some of the strongest evidence came from the Stripe data itself. Stripe sits at a unique vantage point across global commerce, and the charts showed a clear AI-driven inflection since the start of the year. New company formation, payment volume, CLI usage, the growth tied to AI and agents does not look isolated to a small handful of companies. It looks broad, and it looks real. ## Builder demand is immediate Sessions focused on agentic commerce and payments were full. Demand from builders is real and immediate, especially around how to operationalize payments and infrastructure for autonomous systems. That matters because it suggests this is moving from narrative to implementation. Builders are not just curious about the idea. They are actively looking for the tools and systems that let them ship. ## The brand still matters Stripe's brand continues to be a differentiator. Despite its scale, it still feels deliberate and distinct through things like Stripe Press, the Cheeky Pint pub with mini Guinness pours, and the overall tone of the event. The design and production value on the keynotes was great. I will make one exception for the archival footage quick hits. They do need a bigger venue. The show floor, keynotes, sessions, and lunch were all packed, a bit too packed. ## In summary Agentic everything is coming, including payments and commerce. Stripe is going to play a key role in that future. It may be worth thinking about the company less as a payments incumbent and more as a frontier lab for what payments become next. --- ## Related reading - [MPP Session Payments](https://conto.finance/blog/mpp-session-payments) - [Paying for APIs with x402](https://conto.finance/blog/x402-paying-for-apis) - [Enterprise Agentic Payments](https://conto.finance/blog/enterprise-agentic-payments) - [Sign Up for Conto](https://conto.finance) --- An agent with a wallet and no policies is an open checkbook. It can spend any amount, to any address, at any time. That's fine for a hackathon demo. It's not fine for production. Securing an agent that handles real money requires defense in depth: multiple independent controls that each catch a different class of risk. We recommend five layers. ## Layer 1: Spending Limits The most fundamental control. Set how much an agent can spend per transaction, per day, per week, and per month. This catches the obvious failure modes: a runaway loop that drains a wallet, a prompt injection that tricks an agent into overspending, or a misconfigured amount. Even if every other control fails, spending limits cap the blast radius. A reasonable starting point for most agents: - Per-transaction: $100 - Daily: $500 - Monthly: $5,000 These are conservative recommendations for new agents. Note that Conto's system defaults for auto-created wallets are higher (daily: $1,000, weekly: $5,000, monthly: $20,000) — you should tighten them based on the agent's actual needs, not its theoretical maximum. ## Layer 2: Counterparty Controls Spending limits control how much. Counterparty controls determine who. An allowlist restricts the agent to a known set of addresses: verified vendors, internal wallets, trusted API providers. A blocklist catches known bad actors. Both can be combined. For high-security deployments, start with an allowlist-only approach. The agent can only transact with addresses you've explicitly approved. That eliminates the class of attacks where the agent is tricked into sending money to an attacker-controlled address. ## Layer 3: Time Windows Not every agent should be able to spend money at 3 AM on a Saturday. Time window policies restrict when payments can happen: business hours only, weekdays only, or custom schedules. This matters most for agents that interact with external services. If the agent shouldn't be making procurement decisions outside of working hours, enforce it at the policy level instead of relying on the agent's own judgment. ## Layer 4: Micropayment Controls Agents increasingly pay for APIs using protocols like [x402](https://conto.finance/docs/guides/x402-api-payments) and [MPP](https://conto.finance/docs/guides/mpp-session-payments). These are small, high-frequency payments (often fractions of a dollar) that add up quickly. Micropayment-specific policies let you cap per-request amounts, set per-service daily limits, restrict which services the agent can pay for, and limit session deposits. Without these, an agent could rack up hundreds of dollars in API fees before anyone notices. ## Layer 5: Alerts and Monitoring Policies prevent bad transactions. Alerts tell you when something unusual is happening, even if it's technically within limits. Set up alerts for denied payments, high-value transactions, unusual spending patterns, and new counterparty interactions. The point is to give humans visibility into agent financial behavior in real time, not to auto-block. ## Evaluation Order Conto evaluates policies in a specific order: wallet-level limits first, then agent-specific policies. The first denial wins. If any rule at any level blocks the payment, it stops. You can set conservative defaults and then selectively loosen them for specific agents. A procurement agent might get a higher per-transaction limit than a monitoring agent, while both inherit the same counterparty blocklist. ## Start Strict, Loosen Gradually The safest approach is to deploy with tight controls and relax them as you build confidence. It's easier to increase a spending limit after watching the agent's behavior than to recover from overly permissive defaults. The full configuration guide with code examples for every layer is in the docs: [Securing Agents with Policies](https://conto.finance/docs/guides/securing-agents). For quick setup commands, see [Recipes](https://conto.finance/docs/guides/recipes). ### Related - [How to Test Agent Payments Without Losing Real Money](https://conto.finance/blog/testing-agent-payments) - validate your policies on Tempo Testnet - [x402: How Agents Pay for APIs with HTTP](https://conto.finance/blog/x402-paying-for-apis) - deep dive on micropayment controls - [MPP: Session-Based Payments for Agents on Tempo](https://conto.finance/blog/mpp-session-payments) - session deposit and budget controls --- Need help designing a policy strategy for your agents? [Get in touch](mailto:support@conto.finance) or [start building](https://conto.finance). --- Nous Research's Hermes agents can execute payments autonomously. That unlocks a lot, but it also opens the door to real financial risk if there's nothing standing between the agent and the wallet. We're excited to announce our Hermes integration. It's a Conto skill that evaluates every outgoing payment against your spending policies before a single token leaves the wallet. If you're running Hermes agents, you can now enforce per-transaction caps, rolling budgets, counterparty allowlists, human approval thresholds, and 40+ other rule types — all through a single skill install. We're opening up the full policy suite to early adopters at no cost while we collect feedback. [Join our Discord](https://discord.gg/h7rYrpkrRt) if you want to try it out or have questions. ## Why This Matters Autonomous payment ability is table stakes for useful agents. But autonomy without constraints is how wallets get drained — a misconfigured API call, an unexpected loop, a bad recipient address. The usual fix is custom validation logic bolted onto each agent, which tends to be brittle, incomplete, and hard to audit. Conto centralizes that enforcement. You define the rules once, and every payment your Hermes agent attempts gets checked against them before execution. ## The Flow Every payment attempt passes through Conto before any funds move: 1. You configure spending policies in the Conto dashboard 2. Your Hermes agent initiates a payment 3. The skill sends the payment details to Conto's approval endpoint 4. Conto evaluates the request against every active policy 5. The response is `APPROVED`, `DENIED`, or `REQUIRES_APPROVAL` 6. The agent acts accordingly ``` POST /api/sdk/payments/approve ``` A single call covers all your rules — transaction limits, daily caps, counterparty checks, time windows, everything. If the payment doesn't clear, the agent gets back the specific violations so it can report why. ## Installation Hermes supports well-known skill discovery, so setup is one command: ```bash hermes skills install well-known:https://conto.finance/.well-known/skills/conto ``` This pulls down the skill manifest and a `conto-check.sh` helper script into `~/.hermes/skills/conto/`. Then add your SDK key to `~/.hermes/.env`: ```bash CONTO_SDK_KEY=conto_agent_your_key_here CONTO_API_URL=https://conto.finance ``` You can generate keys from the [Conto dashboard](https://conto.finance) under **Agents > SDK Keys > Generate New Key**. Standard keys handle payment evaluation and transaction visibility. Admin keys also allow policy creation and wallet management. ## Policy Types Over 40 rule types are available across several categories: - **Human Approval.** Require manual sign-off for payments above a configurable threshold. - **Spend Limits.** Per-transaction maximums, plus daily, weekly, and monthly rolling caps. Budget allocations for specific use cases. - **Category Controls.** Restrict payments by category — allow only `API_PROVIDER` and `INFRASTRUCTURE`, block `TRAVEL` and `MARKETING`, or any combination across 14 supported categories. - **Counterparty Management.** Maintain allowlists of approved recipient addresses or blocklists of known-bad ones. - **Scheduling.** Limit payments to business hours, specific days of the week, or block them entirely during maintenance windows. - **x402 API Controls.** Set price ceilings per x402 call, cap daily spend per service, or block specific x402 endpoints entirely. You can create policies through structured JSON or plain language: ``` /conto create a policy that limits each transaction to 200 pathUSD ``` ## Wallet Modes The integration supports two custody models: **Provider-managed (Sponge or Privy):** A single API call handles policy evaluation and payment execution together. The wallet provider holds the keys, and Conto orchestrates the full flow. This is the simplest setup if you don't need direct key custody. **External wallet:** Your agent holds its own keys. Conto evaluates the payment and returns an approval. Your agent then executes the transfer independently and confirms back with the transaction hash: ``` POST /api/sdk/payments/{approvalId}/confirm ``` Approval tokens are cryptographically bound to the original request parameters — amount, recipient, and chain — and expire after 10 minutes. They can't be replayed for a different payment. ## Chains Base, Tempo, and Solana are all supported, along with their respective stablecoins. ## Get Started 1. Sign up at [conto.finance](https://conto.finance) 2. Install the Conto skill: ```bash hermes skills install well-known:https://conto.finance/.well-known/skills/conto ``` 3. Connect your Hermes agent in the dashboard 4. Generate an SDK key 5. Add the config to `~/.hermes/.env` 6. Create your first policy ## Resources - [Hermes SDK Reference](https://conto.finance/docs/sdk/hermes) — full API docs for the Hermes integration - [Conto Dashboard](https://conto.finance) — manage agents, policies, and SDK keys - [Skill Manifest](https://conto.finance/.well-known/skills/conto) — well-known endpoint for skill discovery - [Policy Guide](https://conto.finance/docs/policies) — deep dive into all 40+ rule types - [Discord Community](https://discord.gg/h7rYrpkrRt) — feedback, questions, and support - [OpenClaw Integration](https://conto.finance/docs/sdk/openclaw) — if you're also building on OpenClaw --- Your agents can move money. Conto makes sure they do it on your terms. --- Not every payment is a one-shot transaction. When an agent streams data from an API, processes a batch of requests, or interacts with a service over an extended period, paying per-request creates unnecessary overhead. Each payment needs an onchain transaction, each transaction costs gas, and the total friction scales linearly with usage. MPP (Machine Payment Protocol) solves this with sessions. An agent opens a session with a deposit, makes as many requests as needed, and settles once when the session closes. One onchain transaction to open, one to close. Everything in between is offchain. ## How MPP Sessions Work The lifecycle has four steps: 1. **Open** - Agent deposits funds into a session with the service provider 2. **Use** - Agent makes requests, each one debited from the session balance 3. **Close** - Either party closes the session 4. **Settle** - The actual amount consumed is settled onchain; unused deposit is returned This is different from x402, where each request is a separate payment. MPP amortizes the cost across an entire session, which makes it better suited for high-frequency interactions and streaming use cases. ## x402 vs MPP Both protocols enable machine-to-machine payments, but they optimize for different patterns: | | x402 | MPP | |---|---|---| | Payment model | Per-request | Per-session | | Onchain transactions | One per request | Two (open + close) | | Best for | Infrequent, varied APIs | High-frequency, streaming | | Chain | Base, Ethereum | Tempo | | Settlement | Immediate | On session close | An agent that makes one data lookup per hour is a good fit for x402. An agent that streams real-time market data for 8 hours is a good fit for MPP. ## Why Sessions Need Controls Sessions introduce a different risk surface. The deposit amount determines the maximum a session can cost. Without controls, an agent could open sessions with large deposits across multiple services, tying up significant capital. Conto's MPP policy rules address this: - **Session budget** - cap the maximum deposit per session - **Per-request ceiling** - limit how much any single request within a session can cost - **Concurrent session limits** - restrict how many sessions an agent can have open simultaneously - **Session duration limits** - force sessions to close after a maximum time - **Service allowlist** - restrict which services the agent can open sessions with These are evaluated during pre-authorization, before the session deposit is made. ## Running MPP Through Conto The flow mirrors x402 but with session awareness: ```bash # 1. Pre-authorize the session curl -X POST https://conto.finance/api/sdk/mpp/pre-authorize \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 10.00, "recipientAddress": "0xServiceAddress", "resourceUrl": "https://api.service.com/stream", "serviceDomain": "api.service.com", "intent": "session", "depositAmount": 10.00 }' ``` If authorized, the agent opens the session, uses the service, and when it closes, records the settlement back to Conto: ```bash # 2. Record the settled amount (not the deposit) curl -X POST https://conto.finance/api/sdk/mpp/record \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 3.50, "recipientAddress": "0xServiceAddress", "transactionHash": "0xsettlement123...", "sessionId": "mpp_session_xyz", "scheme": "mpp" }' ``` Note that you record the settled amount ($3.50 in this example), not the deposit ($10.00). This keeps spend tracking accurate: the agent's daily total reflects actual consumption, not reserved capital. ## Building for Tempo MPP runs on Tempo, a payments-focused blockchain designed for high-frequency, low-value settlement. Session deposits and settlements use Tempo's native stablecoins (pathUSD, AlphaUSD, etc.), and the chain's low fees make the two-transaction session model practical even for small amounts. The full implementation guide with TypeScript examples and policy configuration is in the docs: [MPP Session Payments](https://conto.finance/docs/guides/mpp-session-payments). Quick-reference commands are on the [Recipes](https://conto.finance/docs/guides/recipes) page. ### Related - [x402: How Agents Pay for APIs with HTTP](https://conto.finance/blog/x402-paying-for-apis) - the per-request alternative for infrequent API calls - [Five Layers of Security for Agents That Spend Money](https://conto.finance/blog/five-layers-agent-security) - where session controls fit in a layered policy strategy --- Exploring session-based payments for your agents? [Start building with Conto](https://conto.finance) or reach out at [support@conto.finance](mailto:support@conto.finance). --- Your agent can make payments. That's the easy part. The hard part is making sure it *should*. Today we're launching our OpenClaw integration, a skill that adds policy enforcement to every payment your AI agent makes. If you're building on OpenClaw, you can now drop in Conto and get per-transaction limits, daily budgets, category restrictions, approval workflows, and 40+ other rule types with quick setup. For a limited time we're offering unlimited access to the full suite of Conto policies to beta testers. We'd love your feedback. [Join our Discord community](https://discord.gg/h7rYrpkrRt) to drop us a note. ## The Problem OpenClaw gives agents the ability to execute payments. That's powerful, but also dangerous. Without guardrails, an agent can drain a wallet on a bad API call, pay a blacklisted address, or blow through a monthly budget in an afternoon. Most teams end up hand-rolling ad-hoc checks that don't scale and miss edge cases. Giving agents the ability to pay requires a lot of trust. Conto helps you add controls to get you there. ## How It Works Conto intercepts every payment attempt before execution and evaluates it against your configured policies: 1. User sets payment policies 2. Agent initiates a payment 3. The Conto skill calls our approval endpoint 4. We evaluate against all active policies 5. We return `APPROVED`, `DENIED`, or `REQUIRES_APPROVAL` 6. The agent proceeds (or doesn't) Most OpenClaw agents use the external wallet flow, where the agent holds its own keys: ``` POST /api/sdk/payments/approve (policy check) POST /api/sdk/payments/{id}/confirm (report tx hash) ``` If you use a Conto-managed wallet (Privy or Sponge), a single call to `/api/sdk/payments/request` handles both the policy check and execution. Every policy evaluated. No payment goes through without passing your rules. ## Setup ### Getting Started Install the Conto skill from [ClawHub](https://clawhub.ai/kwattana/conto): ```bash npx clawhub install conto ``` Or add the skill directly from the manifest: [conto.finance/skill.md](https://conto.finance/skill.md) ### Configuration Add your SDK key to `~/.openclaw/openclaw.json`: ```json { "skills": { "entries": { "conto": { "env": { "CONTO_SDK_KEY": "conto_agent_your_key_here", "CONTO_API_URL": "https://conto.finance" } } } } } ``` Generate keys from the [dashboard](https://conto.finance) under **Agents > SDK Keys > Generate New Key**. Standard keys cover payment evaluation and visibility. Admin keys add policy creation and wallet administration. ## What You Can Enforce We support 40+ rule types across several categories: - **Approval Workflows.** Require human sign-off above a spending threshold. - **Spend Controls.** Per-transaction caps, daily/weekly/monthly limits, budget allocations. - **Category Management.** Whitelist or blacklist specific API providers and payment categories. - **Counterparty Rules.** Block suspicious addresses, allowlist approved vendors. - **Time-Based Restrictions.** Business hours only, weekday-only, maintenance blackout windows. - **API Cost Controls.** Cap per-request costs and limit daily spend per service. Policies can be created via structured input or natural language: ``` /conto create a policy that limits each transaction to 200 pathUSD ``` ## Two Wallet Modes **Integrated (Sponge or Privy):** One API call handles both the policy check and payment execution. Your wallet provider holds the keys (Privy or Sponge, respectively) while Conto orchestrates policy evaluation and execution through the provider. Simplest path if you don't need to hold your own keys. **External wallet:** Your agent holds the keys. Conto approves the payment, your agent executes the transfer, then confirms back with the transaction hash: ``` POST /api/sdk/payments/{REQUEST_ID}/confirm ``` This gives you full custody while still enforcing every policy. ## Chain Support The integration works across Base, Tempo, and Solana with support for their respective stablecoins. ## Get Started 1. Sign up at [conto.finance](https://conto.finance) 2. Install the skill from [ClawHub](https://clawhub.ai/kwattana/conto): ```bash npx clawhub install conto ``` Or add the skill directly from the manifest: [conto.finance/skill.md](https://conto.finance/skill.md) 3. Connect your agent in the dashboard 4. Link your wallet to the agent 5. Generate an SDK key 6. Add the config to your OpenClaw setup 7. Create your first policy Full SDK reference: [conto.finance/docs/sdk/openclaw](https://conto.finance/docs/sdk/openclaw) ### Related - [Adding Spending Policies to Any OpenClaw Agent](https://conto.finance/blog/openclaw-spending-policies) - deeper dive on the policy flow and external wallet mode - [Zero to First Agent Payment in 5 Minutes](https://conto.finance/blog/zero-to-first-payment) - the general Conto setup walkthrough - [Five Layers of Security for Agents That Spend Money](https://conto.finance/blog/five-layers-agent-security) - design a complete policy strategy - [How to Test Agent Payments Without Losing Real Money](https://conto.finance/blog/testing-agent-payments) - validate policies on Tempo Testnet --- Your agents can pay. Now make sure they pay correctly. --- [OpenClaw](https://github.com/openclaw/openclaw) gives agents the ability to act. Conto gives organizations control over how those agents spend money. The Conto skill for OpenClaw connects both: it hooks into the payment flow and evaluates every transaction against 40+ policy rule types before a dollar moves onchain. ## The Problem With Uncontrolled Agent Wallets When you give an OpenClaw agent a wallet, it can pay anyone, any amount, at any time. For personal projects and hackathons, this is fine. For anything with real stakes (a company deploying agents, a team managing budgets, a product handling user funds) it's a serious gap. The common workaround is to limit the wallet balance. Give the agent $50 and let it spend freely. But that doesn't distinguish between a legitimate $50 vendor payment and a $50 payment to a malicious address. It doesn't enforce business hours. It doesn't require approval for high-value transactions. It's a balance cap, not a policy engine. ## How the Conto Skill Works The skill sits between the agent's intent to pay and the actual onchain transfer: ``` Agent decides to pay → Skill calls Conto → Policies evaluated → Pay or deny ``` When the agent wants to send money, the skill calls Conto's payment request endpoint. Conto evaluates every policy assigned to the agent. If all rules pass, the payment proceeds. If any rule fails, the payment is blocked and the agent receives the specific violations. The agent doesn't need to know about the policies. It tries to pay. The control layer handles the rest. ## Setting It Up Four steps: 1. **Connect your agent in Conto** - create an agent record and assign policies 2. **Link your wallet** - register your wallet address so Conto can track spend limits 3. **Generate an SDK key** - Standard for payment-only, Admin if you want the agent to manage its own policies 4. **Add to OpenClaw config** - set the SDK key and API URL in `openclaw.json` Once configured, the agent can interact with policies through natural language: ``` /conto create a policy that limits each transaction to 200 pathUSD /conto list my policies Send 50 pathUSD to 0x742d... on Tempo ``` The last command triggers the full flow: the skill calls Conto, policies are evaluated, the agent transfers onchain, and the skill confirms the transaction back to Conto for tracking. ## What You Can Control The skill supports all of Conto's policy types: - **Spending limits** - per-transaction, daily, weekly, monthly caps - **Counterparty controls** - allowlists and blocklists for recipient addresses - **Time restrictions** - business hours, allowed days, blackout windows - **Category controls** - allow or block specific payment categories - **Approval thresholds** - require human sign-off above a certain amount - **Velocity limits** - rate-limit transaction frequency - **x402 and MPP controls** - govern micropayment spending Policies can be created through the Conto dashboard, the API, or through the OpenClaw agent itself (with an Admin SDK key). They're evaluated independently, and the first denial wins. ## The External Wallet Flow OpenClaw agents typically hold their own keys, so they use Conto's external wallet flow: 1. **Approve** - skill calls Conto to check policies 2. **Transfer** - agent sends the payment onchain using its own keys 3. **Confirm** - skill reports the transaction hash back to Conto Conto never needs access to the agent's private keys. It only provides the policy decision. The agent keeps full custody of its wallet while getting spending controls. ## Dashboard Visibility Every payment, approved or denied, shows up in the Conto dashboard. Confirmed transactions include explorer links and full audit trails. Denied attempts appear in the alerts view with the specific policy violations that blocked them. For organizations running multiple OpenClaw agents, this is a single view of all agent financial activity: which agents are spending, how much, to whom, and whether any are hitting policy limits. The full setup guide with an end-to-end example is in the docs: [OpenClaw Skill](https://conto.finance/docs/sdk/openclaw). Install from [ClawHub](https://clawhub.ai/kwattana/conto) with `npx clawhub install conto`, or add the skill directly from the manifest at [conto.finance/skill.md](https://conto.finance/skill.md). For quick policy setup commands, check the [Recipes](https://conto.finance/docs/guides/recipes) page. ### Related - [Zero to First Agent Payment in 5 Minutes](https://conto.finance/blog/zero-to-first-payment) - the general Conto setup walkthrough - [Five Layers of Security for Agents That Spend Money](https://conto.finance/blog/five-layers-agent-security) - design a complete policy strategy - [How to Test Agent Payments Without Losing Real Money](https://conto.finance/blog/testing-agent-payments) - validate policies on Tempo Testnet --- Running OpenClaw agents that handle money? [Add spending controls with Conto](https://conto.finance) or reach out at [support@conto.finance](mailto:support@conto.finance). --- If your first test of an agent's spending controls happens in production, you've already lost. Agentic payments need the same testing rigor as any other production system, arguably more since the failure mode is real money leaving real wallets. Conto supports full testing on Tempo Testnet with free pathUSD tokens. Every policy type, every rule evaluation, every approval flow works identically to mainnet. The only difference is that no real value moves. ## Five Scenarios You Should Test Before any agent goes live, run these five scenarios. Each one validates a different part of the policy engine: **1. Approved payment** - A payment within all limits. Confirms the basic flow works end to end. **2. Denied by spending limit** - A payment that exceeds a per-transaction or daily cap. Validates that the agent stops when it should. **3. Requires human approval** - A payment above the approval threshold. Validates the pause-and-wait flow for high-value transactions. **4. Blocked counterparty** - A payment to an address not on the allowlist (or on a blocklist). Validates counterparty controls. **5. Time window restriction** - A payment attempted outside business hours. Validates temporal controls. If all five pass, your policy configuration is enforcing correctly. If any fail unexpectedly, you've found a misconfiguration before it matters. ## The Testing Pattern Each test follows the same structure: 1. Set up a policy targeting the scenario 2. Attempt a payment that should trigger the policy 3. Verify the response matches expectations (approved, denied, or requires_approval) 4. Check the dashboard for the correct audit trail The request endpoint is the same one used in production: ```bash curl -X POST https://conto.finance/api/sdk/payments/request \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 10, "recipientAddress": "0x...", "purpose": "Test payment" }' ``` The response tells you what happened and why. Denied payments include a `violations` array with every rule that failed, so you know which policy blocked the transaction and what the threshold was. ## From Test to Production Moving from testnet to production is a configuration change, not a code change. Swap the wallet from Tempo Testnet to your production chain, review your policies one more time, and deploy. The full testing guide with all five scenarios, policy setup commands, and a production migration checklist is in the docs: [Testing Payments Safely](https://conto.finance/docs/guides/testing-payments). Quick-reference curl commands are on the [Recipes](https://conto.finance/docs/guides/recipes) page. ### Related - [Zero to First Agent Payment in 5 Minutes](https://conto.finance/blog/zero-to-first-payment) - set up Conto and make your first payment - [Five Layers of Security for Agents That Spend Money](https://conto.finance/blog/five-layers-agent-security) - design a production policy stack --- Ready to test your agent's payment controls? [Get started on Tempo Testnet](https://conto.finance) or reach out at [support@conto.finance](mailto:support@conto.finance). --- Here's what shipped at Conto this week. We've updated the AI assistant (name pending, any suggestions?), platform hardening, and admin tooling, with some docs cleanup on the side. ## Unlocking the assistant The assistant got a lot of attention this week. You can now set and manage policies directly in the chat. This is in addition to querying alerts and agent status with natural language. ## Security and reliability We shipped a batch of changes aimed at authentication paths, approval flows, and audit log integrity. On the reliability side, we continued with enterprise-readiness work to support critical workflows. ## Admin and operator tooling A few practical wins for the people running Conto day-to-day: - Better filtering on the audit log - UX fixes across onboarding, transactions, and policy screens - Machine spend analytics, so you can see where agent dollars are actually going The spend analytics view is worth a look if you've been flying blind on agent usage. ## Docs and developer experience We refreshed the public docs site, and expanded the growth and hardening guides. --- ## Related reading - [Five Layers of Agent Security](https://conto.finance/blog/five-layers-agent-security) - [Enterprise Agentic Payments](https://conto.finance/blog/enterprise-agentic-payments) - [OpenClaw Spending Policies](https://conto.finance/blog/openclaw-spending-policies) - [Zero to First Payment](https://conto.finance/blog/zero-to-first-payment) - [Sign Up for Conto](https://conto.finance) - [Join the Discord](https://discord.gg/h7rYrpkrRt) --- Here's what shipped at Conto last week. We focused a lot on Conto Pay, our new agentic payments solution for companies that want to ship agent-driven payment flows without building the whole stack themselves. The agentic payments landscape is vast and rapidly growing and we've been hearing a lot from teams that find it challenging to navigate. How do you pick the right providers from a market map of 100s of different vendors in a market that is changing every day? We're working to make that easier. ## Conto Pay as a hosted payments solution Conto Pay gives teams a single place to fund wallets, run payments, and keep agent activity under control, without standing up wallets, stablecoin rails, payment workflows, and operational tooling on their own. This week's updates push it further in that direction. ## More of the payment workflow now happens in chat Conto Pay now supports a broader set of payment actions directly from its dedicated chat surface. Teams can refresh and fund wallets, inspect payments, cancel or retry payment actions, and manage payment controls from inside Conto Pay chat. The full payment workflow lives in one place, with fewer reasons to drop out of the flow to handle routine operational steps somewhere else. ## A more complete hosted experience The Conto Pay experience now supports a fuller payment console workflow, so teams using Conto Pay as their hosted payments layer get a more capable, consistent surface for managing wallets, payments, and controls in one place. If you want to capitalize on agentic payments, there's a lot of complexity around the infrastructure needed to get there. Conto Pay is built to handle that end to end so your team can focus on your product. Our goal is to bridge the gap between agentic payments as an interesting idea and what it actually looks like in reality. ## Broader stablecoin coverage We expanded USDT support across the chains Conto already supports. For teams using Conto to coordinate agent payment flows, that means more flexibility in how they fund wallets and move money, and a wider set of stablecoin options to work with out of the box. --- ## Related reading - [Enterprise Agentic Payments](https://conto.finance/blog/enterprise-agentic-payments) - [Agentic Payments Wild West](https://conto.finance/blog/agentic-payments-wild-west) - [Zero to First Payment](https://conto.finance/blog/zero-to-first-payment) - [Sign Up for Conto](https://conto.finance) - [Join the Discord](https://discord.gg/h7rYrpkrRt) --- The HTTP 402 status code ("Payment Required") has existed since 1997. It was reserved for future use. Nearly three decades later, it finally has a real implementation. The [x402 protocol](https://conto.finance/docs/sdk/x402-payments) gives 402 actual payment semantics. When an agent calls a paid API and gets a 402 response, the body includes payment requirements: how much, to whom, on which chain. The agent pays, retries the request with a payment header, and gets the data. No API keys, no subscriptions, no invoices. Just HTTP and money. ## The Flow An x402 interaction has three steps: 1. **Agent calls API** - gets a 402 with payment requirements 2. **Agent pays** - sends USDC onchain to the facilitator address 3. **Agent retries** - includes the payment signature, gets the response For the agent, it's one extra round trip. For the API provider, every request is paid for at the time of use. ## Why This Needs Controls The simplicity of x402 is also its risk. An agent that can pay for any API, at any price, with no limits, is a cost management problem waiting to happen. Consider an agent that queries a paid data API in a loop. Each request costs $0.05. Harmless in isolation. At 1,000 requests per hour, that's $50/hour, or $1,200/day. Without controls, nobody notices until the monthly bill arrives. Conto's x402 policy rules address this directly: - **Per-request ceiling** - cap the maximum any single x402 payment can cost - **Per-service daily limit** - cap total spending on a specific API domain - **Service allowlist** - restrict which APIs the agent can pay for at all - **Velocity limits** - rate-limit how many paid requests an agent can make per hour These rules are evaluated during the pre-authorization step, before any money moves. ## Pre-Authorize, Pay, Record When running x402 payments through Conto, the agent adds a pre-authorization step before paying: ```bash # 1. Pre-authorize against policies curl -X POST https://conto.finance/api/sdk/x402/pre-authorize \ -H "Authorization: Bearer $CONTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 0.05, "recipientAddress": "0xFacilitator", "resourceUrl": "https://api.example.com/data", "serviceDomain": "api.example.com" }' ``` If authorized, the agent proceeds with the payment. After the onchain transaction completes, it records the payment back to Conto for tracking and spend accounting. This three-step flow (pre-authorize, pay, record) means every x402 payment is policy-checked, onchain verified, and fully auditable. ## The Bigger Picture x402 is one of the first payment protocols built for machine-to-machine commerce. It removes the friction of API key management and subscription billing, replacing it with pay-per-use at the protocol level. But protocol-level payments without controls create new risks. Conto adds the governance layer: visibility and spending limits over agents' x402 usage, just like you'd govern traditional payments. The full implementation guide with TypeScript examples and policy configuration is in the docs: [Paying for APIs with x402](https://conto.finance/docs/guides/x402-api-payments). Quick-reference commands are on the [Recipes](https://conto.finance/docs/guides/recipes) page. ### Related - [MPP: Session-Based Payments for Agents on Tempo](https://conto.finance/blog/mpp-session-payments) - the session-based alternative for high-frequency use cases - [Five Layers of Security for Agents That Spend Money](https://conto.finance/blog/five-layers-agent-security) - where x402 controls fit in a layered policy strategy --- Want to add x402 controls to your agents? [Start building with Conto](https://conto.finance) or reach out at [support@conto.finance](mailto:support@conto.finance). --- Getting an agent to make its first onchain payment shouldn't take a weekend. With Conto, it's four steps: create a wallet, connect your agent, generate an SDK key, and pay. For the full copy-paste setup walkthrough, see the [Connect Agent & Wallet](https://conto.finance/docs/quickstart/first-agent) quickstart. ## The Setup Every agent payment in Conto flows through three components: 1. **Wallet** - where the money lives. Your wallet provider holds the keys (Sponge, Privy, or your own for external wallets). 2. **Agent** - the identity making decisions, connected in the Conto dashboard 3. **SDK Key** - scoped credentials the agent uses to talk to Conto You create each through the dashboard or via the API. The wallet gets provisioned onchain (Base, Tempo, or Solana), linked to the agent with delegation limits, and the agent gets an SDK key with the appropriate scopes. Once configured, your agent can verify its setup with a single call: ```bash curl https://conto.finance/api/sdk/setup \ -H "Authorization: Bearer $CONTO_API_KEY" ``` If the response shows an active agent, linked wallets, and assigned policies, you're ready to pay. ## The Payment Flow Conto uses a two-step payment model: **request** then **execute**. The request step evaluates policies. Every rule assigned to the agent (spending limits, counterparty restrictions, time windows, approval thresholds) is checked before the payment is approved. If anything fails, the payment is denied with the specific violations listed. The execute step sends money onchain. For integrated wallets (Privy or Sponge), Conto orchestrates the transfer through the wallet provider. For external wallets, the agent transfers itself and confirms back. Separating these steps means you can test whether a payment would be allowed without moving money. The policy engine always sits between intent and execution. ## Why This Matters Most agent frameworks skip financial controls entirely. They give the agent a wallet, maybe a card, and hope for the best. That works for demos. It falls apart when you're running dozens of agents in production with real budgets. The first payment is the starting point. Once the pattern is established (request, evaluate, execute) you can layer on spending limits, counterparty allowlists, time restrictions, and approval workflows without changing how the agent interacts with Conto. The full step-by-step setup guide is in the docs: [Connect Agent & Wallet](https://conto.finance/docs/quickstart/first-agent). For quick copy-paste snippets, check the [Recipes](https://conto.finance/docs/guides/recipes) page. ### Related - [How to Test Agent Payments Without Losing Real Money](https://conto.finance/blog/testing-agent-payments) - validate policies on Tempo Testnet before going live - [Five Layers of Security for Agents That Spend Money](https://conto.finance/blog/five-layers-agent-security) - design a complete policy strategy --- Building with agents that spend money? [Get started with Conto](https://conto.finance) or reach out at [support@conto.finance](mailto:support@conto.finance). --- ## API Reference (auto-generated from OpenAPI spec) ### Authentication User registration and authentication - `POST /api/auth/register` — Register a new account Create a new user account and organization, then send an email verification link. Privileged API credentials are not issued until the account is verified. ### Agents AI agent management - `GET /api/agents` — List all agents Retrieve all agents for the current organization. - `POST /api/agents` — Create a new agent Create an AI agent that can be linked to wallets and policies for controlled payments. - `GET /api/agents/{id}` — Get agent by ID Get full agent details including linked wallets, policies, and transaction counts. - `PATCH /api/agents/{id}` — Update agent - `DELETE /api/agents/{id}` — Delete agent - `GET /api/agents/{id}/wallets` — List wallets linked to agent - `POST /api/agents/{id}/wallets` — Link wallet to agent Link a wallet to an agent with delegation type, spend limits, and time window controls. - `PATCH /api/agents/{id}/wallets/{walletId}` — Update agent-wallet link Update spend limits, delegation type, or time window for an agent-wallet link. - `DELETE /api/agents/{id}/wallets/{walletId}` — Unlink wallet from agent - `GET /api/agents/{id}/policies` — List policies assigned to agent - `POST /api/agents/{id}/policies` — Assign policy to agent - `DELETE /api/agents/{id}/policies` — Unassign policy from agent Uses query parameter: DELETE /api/agents/{id}/policies?policyId={policyId} - `GET /api/agents/{id}/sdk-keys` — List SDK keys for agent - `POST /api/agents/{id}/sdk-keys` — Generate SDK key for agent Generate a new SDK API key for the agent. The full key is returned only once. Key format: conto_agent_xxx... - `POST /api/agents/{id}/freeze` — Freeze an agent Immediately suspend an agent and block all transactions. Optionally freeze associated wallets. Creates a FreezeEvent audit record. - `GET /api/agents/{id}/freeze` — Get freeze status and configuration Returns the agent freeze status, behavioral counters, stored freeze config, and the effective config (stored merged with defaults). - `POST /api/agents/{id}/unfreeze` — Unfreeze an agent Restore a frozen agent to ACTIVE status. Optionally unfreeze associated wallets and reset behavioral counters. - `PATCH /api/agents/{id}/freeze-config` — Update freeze configuration Update auto-freeze thresholds for an agent. Omitted fields keep their current values. Thresholds define when automatic freezing triggers fire. - `GET /api/agents/{id}/freeze-history` — Get agent freeze event history Paginated list of freeze and unfreeze events for this agent. ### Wallets Wallet management, provisioning, and funding - `GET /api/wallets` — List all wallets List all wallets for the organization with linked agents and policies. - `POST /api/wallets` — Create a new wallet Create a new wallet. By default creates a PRIVY-custodied EOA wallet on EVM. You can also attach an existing Privy-backed wallet by providing custodyType=PRIVY with externalWalletId and address, or register a self-custodied wallet with custodyType=EXTERNAL and address/importAddress. Requests are idempotent per externalWalletId + chainId or address + chainId within the organization. - `GET /api/wallets/{id}` — Get wallet by ID - `PATCH /api/wallets/{id}` — Update wallet - `POST /api/wallets/{id}/provision` — Provision wallet onchain Provision a Sponge-custodied wallet: links to the platform Sponge account, syncs a real blockchain address and balance, and marks the wallet ready for SDK-initiated payments. No request body required. ### Policies Spending policy configuration and rules - `GET /api/policies` — List all policies List all policies for the organization with their rules and assignments. - `POST /api/policies` — Create a new policy Create a policy with optional inline rules and agent assignments. When `rules` and/or `agentIds` are provided, everything is created in a single atomic transaction — if any step fails, nothing is created. You can also create a policy shell first and add rules separately via POST /api/policies/{id}/rules. - `GET /api/policies/{id}` — Get policy by ID - `PATCH /api/policies/{id}` — Update policy - `DELETE /api/policies/{id}` — Delete policy Permanently delete a policy and remove it from all agents. Consider deactivating (PATCH with isActive: false) instead if you want to preserve the policy configuration. - `GET /api/policies/{id}/rules` — List rules for a policy - `POST /api/policies/{id}/rules` — Add rules to a policy Add one or more rules to a policy. Rules are evaluated using AND logic — all must pass for a payment to be approved. ### Transactions Transaction history and management - `GET /api/transactions` — List transactions List payment transactions for the organization. Filter by status, agent, or amount. Transaction statuses: PENDING, APPROVED, DENIED, CONFIRMED, FAILED. ### SDK Payments SDK endpoints for agent payment flow (request, execute, status) - `POST /api/sdk/payments/request` — Request payment authorization Request authorization for a payment from an AI agent. Evaluates all assigned policies and wallet-level spend limits. Returns APPROVED, DENIED, or REQUIRES_APPROVAL. - `GET /api/sdk/payments/{requestId}` — Get payment request status Check the status of a payment request. - `POST /api/sdk/payments/{requestId}/execute` — Execute approved payment Execute a previously approved payment request. The request must have status APPROVED and not be expired. Executes the onchain transaction via Sponge wallet. - `POST /api/sdk/payments/approve` — Approve an external wallet payment Request policy approval for a payment the agent will execute via its own external wallet. Returns an approval token to use when confirming. Requires `payments:approve` scope. - `POST /api/sdk/payments/{requestId}/confirm` — Confirm external wallet payment execution Confirm that an externally approved payment was executed by providing the transaction hash and approval token. Requires `payments:confirm` scope. ### SDK Agent SDK endpoints for agent self-service - `GET /api/sdk/all` — Get complete agent data Returns everything about the authenticated agent in a single call: profile, wallets, policies, counterparties, transactions, alerts, analytics, and capabilities. Use the `include` query parameter to cherry-pick specific sections. - `GET /api/sdk/agents/me` — Get agent profile Get the authenticated agent profile and summary statistics. - `GET /api/sdk/setup` — Get agent bootstrap configuration Returns comprehensive agent configuration including profile, wallets, policies, counterparties, scopes, available endpoints, and capabilities. Designed for initial SDK bootstrap. ### SDK Wallets SDK endpoints for agent wallet access - `GET /api/sdk/wallets` — List agent wallets List all wallets assigned to the authenticated agent. - `GET /api/sdk/wallets/{id}` — Get wallet details ### SDK Transactions SDK endpoints for agent transaction history - `GET /api/sdk/transactions` — List agent transactions - `GET /api/sdk/transactions/{id}` — Get transaction details - `POST /api/sdk/transactions/{id}/retry` — Retry a failed transaction Queue a failed transaction for retry. Only transactions with FAILED status can be retried. Requires `transactions:write` scope. ### SDK Policies SDK endpoints for agent policy inspection - `GET /api/sdk/policies` — List agent policies List policies that govern the authenticated agent, with effective limits. - `GET /api/sdk/policies/exceptions` — List policy exception requests List policy exception requests submitted by this agent. Requires `policies:exceptions` scope. - `POST /api/sdk/policies/exceptions` — Request a policy exception Submit a request for a policy exception (e.g., whitelist a counterparty, increase spend limit). Requires `policies:exceptions` scope. ### Counterparties Counterparty management and trust - `GET /api/counterparties` — List counterparties List counterparties (payment recipients) with their trust levels and transaction history. Trust levels: TRUSTED, VERIFIED, UNKNOWN, BLOCKED. - `POST /api/counterparties` — Create a counterparty ### API Keys Organization API key management - `GET /api/api-keys` — List organization API keys - `POST /api/api-keys` — Create organization API key Create an org-level API key with scoped permissions. The full key is returned only once. ### Alerts Alert management - `GET /api/alerts` — List alerts List alerts and notifications for the organization. Alert types include: SPEND_LIMIT_WARNING, SPEND_LIMIT_EXCEEDED, UNUSUAL_ACTIVITY, FAILED_TRANSACTION, POLICY_VIOLATION, NEW_COUNTERPARTY, HIGH_VALUE_TRANSACTION, AGENT_SUSPENDED, WALLET_LOW_BALANCE, BLOCKED_ADDRESS. Severities: INFO, WARNING, CRITICAL. ### x402 x402 protocol pre-authorization, recording, and analytics - `POST /api/sdk/x402/pre-authorize` — Pre-authorize an x402 payment Check policy approval before signing an x402 payment. - `POST /api/sdk/x402/record` — Record x402 payment(s) Record a single x402 payment or a batch of micropayments. - `GET /api/sdk/x402/services` — List x402 services used by agent - `GET /api/sdk/x402/budget` — Check x402 budget remaining Get x402 budget remaining, burn rate, and projections. ### mpp Machine Payment Protocol pre-authorization, recording, and analytics - `POST /api/sdk/mpp/pre-authorize` — Pre-authorize an MPP payment Check policy approval before creating an MPP credential. The agent sends the WWW-Authenticate: Payment challenge contents and Conto evaluates policies without executing anything. - `POST /api/sdk/mpp/record` — Record MPP payment(s) Record a single MPP payment or a batch of micropayments. Used by agents that handle MPP payments directly and want to report them to Conto for spend tracking and policy enforcement. - `GET /api/sdk/mpp/services` — List MPP services used by agent List all MPP services the agent has interacted with, including spend summaries, price trends, and known endpoints. - `GET /api/sdk/mpp/budget` — Check MPP budget remaining Get MPP budget remaining, burn rate, and projections. Includes daily/weekly/monthly spend, policy limits, and hours until budget exhaustion. ### SDK A2A Payments Agent-to-agent payment requests and resolution - `POST /api/sdk/a2a/request` — Create an A2A payment request Request payment from another Conto agent. The requesting agent is asking the target agent to send them funds. Target can be specified by agent ID or wallet address. - `GET /api/sdk/a2a/requests` — List A2A payment requests List payment requests for the authenticated agent. Filter by direction (incoming/outgoing) and status. - `GET /api/sdk/a2a/requests/{id}` — Get A2A payment request details Get details of a specific A2A payment request, including direction and status. - `POST /api/sdk/a2a/requests/{id}/execute` — Execute an approved A2A payment request Execute a previously approved A2A payment request. The request must be in an approved state. - `GET /api/sdk/a2a/resolve` — Resolve a wallet address to a Conto agent Check if a wallet address belongs to a registered Conto agent. Useful for verifying recipients before sending A2A payment requests. - `GET /api/sdk/a2a/stats` — Get A2A payment statistics Get aggregate A2A payment statistics for the authenticated agent. ### SDK Cards Card payment approval and confirmation (BYOC flow) - `POST /api/sdk/cards/approve` — Request card payment approval Request policy approval before charging a card (BYOC flow step 1). Evaluates per-transaction limits, daily/weekly/monthly limits, time windows, and merchant restrictions. Requires `payments:approve` scope. - `POST /api/sdk/cards/{id}/confirm` — Confirm card payment execution Confirm that a card payment was charged externally (BYOC flow step 2). Requires the approval token from the approve step. Requires `payments:confirm` scope. ### SDK Counterparties SDK endpoints for counterparty and network trust management - `GET /api/sdk/counterparties` — List agent counterparties List counterparties the agent has interacted with, including trust levels and transaction history. Requires `counterparties:read` scope. - `POST /api/sdk/counterparties` — Create a counterparty Register a new counterparty (payment recipient). Requires `counterparties:write` scope. - `GET /api/sdk/counterparties/{id}` — Get counterparty details Get detailed counterparty info including trust relationship and recent transactions. Requires `counterparties:read` scope. - `PATCH /api/sdk/counterparties/{id}` — Update a counterparty Update counterparty details. Requires `counterparties:write` scope. - `GET /api/sdk/network/trust/{address}` — Get network trust for an address Look up trust information for any wallet address on the Conto network. Returns entity type, trust scores, relationship history, and any flags. Requires `network:read` scope. ### SDK Alerts & Analytics SDK endpoints for alerts, analytics, audit logs, approval requests, spending limits, and rate limits - `GET /api/sdk/alerts` — List agent alerts List alerts for the authenticated agent. Requires `alerts:read` scope. Filter by status and severity. - `GET /api/sdk/alerts/{id}` — Get alert details Get details of a specific alert. Requires `alerts:read` scope. - `PATCH /api/sdk/alerts/{id}` — Acknowledge or resolve an alert Update an alert status by acknowledging or resolving it. Requires `alerts:write` scope. - `GET /api/sdk/analytics` — Get spending analytics Get spending analytics for the authenticated agent including volume trends, category breakdowns, and top merchants. Requires `analytics:read` scope. - `GET /api/sdk/audit-logs` — List audit logs Get audit logs of actions performed by or affecting this agent. Requires `audit:read` scope. - `GET /api/sdk/approval-requests` — List pending approval requests List pending approval requests for the agent, including payments awaiting human approval and incoming A2A payment requests. - `GET /api/sdk/spending-limits` — Get spending limits and usage Get per-wallet spending limits and current usage for the authenticated agent. Requires `wallets:read` scope. - `GET /api/sdk/rate-limits` — Get rate limit status Get current rate limit status, usage counts, and per-endpoint rate limits for the authenticated agent. ### Other - `GET /api/health` — Health check Check API health status. ### Safety - `GET /api/freeze-events` — List organization-wide freeze events Paginated list of all freeze and unfreeze events across all agents in the organization. ## Key Concepts Summary ### Registration POST /api/auth/register — Creates user, organization, and returns a pre-generated org API key. Required fields: name, email, password, organizationName ### Authentication - SDK Keys: `conto_agent_xxx...` - For agent payment operations (per-agent, generated via POST /api/agents/{id}/sdk-keys) - API Keys: `conto_xxx...` - For full platform API access (org-level, generated via POST /api/api-keys) ### Agent Types OPENAI_ASSISTANT, ANTHROPIC_CLAUDE, LANGCHAIN, AUTOGPT, CUSTOM Field name: `agentType` (not `type`) ### SDK Scopes Default: `payments:request`, `payments:execute` Additional (opt-in): `payments:confirm`, `wallets:read`, `transactions:read`, `transactions:write`, `policies:read`, `policies:exceptions`, `counterparties:read`, `counterparties:write`, `alerts:read`, `alerts:write`, `agents:read`, `analytics:read`, `network:read`, `audit:read` ### Payment Flow 1. Agent calls `payments.request()` with amount, recipient, purpose 2. Conto evaluates all policies (spend limits, time windows, counterparty rules) 3. Returns APPROVED, DENIED, or REQUIRES_APPROVAL 4. If approved, agent calls `payments.execute()` with requestId 5. Transaction is submitted to blockchain and confirmed 6. Alternative: Use `autoExecute: true` in step 1 to request + execute in one call (returns status: "EXECUTED" with txHash) ### Wallet Selection Wallets are selected by custody priority: PRIVY > SPONGE > SMART_CONTRACT > EXTERNAL. Executable wallets (PRIVY, SPONGE) are preferred. Response includes `walletSelectionReason` and `currency`/`chain` info. ### Custody Providers - PRIVY (Default): Enterprise-grade key management with policy controls - SPONGE: Fast setup with gas sponsorship for stablecoins - EXTERNAL: Externally managed wallet (import address required) - SMART_CONTRACT: Onchain enforcement (coming soon) ### Wallet Lifecycle 1. Create wallet via POST /api/wallets (default: PRIVY custody, EOA, EVM) 2. Provision wallet via POST /api/wallets/{id}/provision (gets blockchain address) 3. Link wallet to agent via POST /api/agents/{agentId}/wallets (set spend limits) 4. Agent can now request and execute payments ### Policy Types & Rule Types Policies are created via `POST /api/policies` then rules added via `POST /api/policies/{id}/rules`. Each rule has: `ruleType`, `operator`, `value` (JSON string), `action` (ALLOW/DENY/REQUIRE_APPROVAL). - SPEND_LIMIT: Rules: `MAX_AMOUNT`, `DAILY_LIMIT`, `WEEKLY_LIMIT`, `MONTHLY_LIMIT`, `BUDGET_CAP` - TIME_WINDOW: Rules: `TIME_WINDOW` (BETWEEN operator, HH:MM format), `DAY_OF_WEEK` (IN_LIST), `BLACKOUT_PERIOD`, `DATE_RANGE` - COUNTERPARTY: Rules: `ALLOWED_COUNTERPARTIES` (IN_LIST, ALLOW), `BLOCKED_COUNTERPARTIES` (IN_LIST, DENY), `TRUST_SCORE`, `COUNTERPARTY_STATUS` - CATEGORY: Rules: `ALLOWED_CATEGORIES` (IN_LIST, ALLOW), `BLOCKED_CATEGORIES` (IN_LIST, DENY) - VELOCITY: Rules: `VELOCITY_LIMIT` (value: `{"maxCount": N, "period": "HOUR"}` or `{"maxAmount": N, "period": "DAILY"}`) - GEOGRAPHIC: Rules: `GEOGRAPHIC_RESTRICTION` (IN_LIST, DENY) - APPROVAL_THRESHOLD: Rules: `REQUIRE_APPROVAL_ABOVE` (GREATER_THAN, REQUIRE_APPROVAL) - CONTRACT/DeFi: Rules: `CONTRACT_ALLOWLIST`, `PROTOCOL_ALLOWLIST` ### Multi-Policy AND Logic All assigned policies must pass. First DENY stops evaluation immediately. DELETE policy from agent: `DELETE /api/agents/{id}/policies?policyId=xxx` (query param, not path param) ### AgentWallet-Level Limits Spend limits also exist at wallet level: `spendLimitPerTx`, `spendLimitDaily`, `spendLimitWeekly`, `spendLimitMonthly` (null = unlimited). Set via `POST /api/agents/{id}/wallets` or `PATCH /api/agents/{id}/wallets/{walletId}`. Agent wallet endpoints support both session auth and org API key auth. ### Operators Numeric: EQUALS/EQ, NOT_EQUALS/NEQ, GREATER_THAN/GT, GREATER_THAN_OR_EQUAL/GTE, LESS_THAN/LT, LESS_THAN_OR_EQUAL/LTE List: IN/IN_LIST, NOT_IN/NOT_IN_LIST Range: BETWEEN, NOT_BETWEEN ### Violation Types INSUFFICIENT_BALANCE, PER_TX_LIMIT, DAILY_LIMIT, WEEKLY_LIMIT, MONTHLY_LIMIT, TIME_WINDOW, BLOCKED_COUNTERPARTY, WHITELIST_VIOLATION, CATEGORY_RESTRICTION, VELOCITY_LIMIT, GEOGRAPHIC_RESTRICTION, BUDGET_EXCEEDED, EXPIRED_PERMISSION, CONTRACT_NOT_ALLOWED, BLACKOUT_PERIOD ### Trust Levels - TRUSTED (0.75-1.0): High confidence, minimal restrictions - VERIFIED (0.5-0.75): Established relationship - UNKNOWN (0.2-0.5): Limited history, requires scrutiny - BLOCKED (0.0-0.2): High risk, transactions blocked ## Quick Code Examples ### Initialize SDK ```typescript import { Conto } from '@conto/sdk'; const conto = new Conto({ apiKey: process.env.CONTO_API_KEY }); ``` ### Make a Payment ```typescript const result = await conto.payments.pay({ amount: 50.00, recipientAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f...', recipientName: 'OpenAI', purpose: 'API credits', category: 'AI_SERVICES' }); console.log('TX Hash:', result.txHash); ``` ### Single-Call Payment (autoExecute) ```typescript const result = await fetch('/api/sdk/payments/request', { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ amount: 50, recipientAddress: '0x...', purpose: 'API credits', autoExecute: true }) }).then(r => r.json()); // result.status === 'EXECUTED', result.execution.txHash, result.currency, result.chain ``` ### Bootstrap Agent (GET /api/sdk/setup) ```typescript const config = await fetch('/api/sdk/setup', { headers: { 'Authorization': `Bearer ${apiKey}` } }).then(r => r.json()); // config.agent, config.wallets, config.policies, config.counterparties, config.scopes, config.capabilities ``` ### Create Counterparty (POST /api/sdk/counterparties) ```typescript // Requires scope: counterparties:write await fetch('/api/sdk/counterparties', { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'Vendor', address: '0x...', type: 'VENDOR', category: 'AI_SERVICES' }) }).then(r => r.json()); ``` ### Request Policy Exception (POST /api/sdk/policies/exceptions) ```typescript // Requires scope: policies:exceptions await fetch('/api/sdk/policies/exceptions', { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 'ADD_TO_WHITELIST', reason: 'Need to pay new vendor', details: { counterpartyAddress: '0x...' } }) }).then(r => r.json()); ``` ### Two-Step Payment (Request then Execute) ```typescript // Step 1: Request authorization const request = await conto.payments.request({ amount: 100, recipientAddress: '0x...', purpose: 'Infrastructure costs' }); // Step 2: Execute if approved if (request.status === 'APPROVED') { const result = await conto.payments.execute(request.requestId); } ``` ### Create Wallet via API ```bash curl -X POST https://conto.finance/api/wallets \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Operations Wallet", "custodyType": "SPONGE", "chainType": "EVM", "chainId": "42431" }' ``` ## Support - Dashboard: https://conto.finance - Documentation: https://conto.finance/docs - Email: support@conto.finance