SDK Examples
Complete examples for common integration patterns.Basic Payment
Copy
Ask AI
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
Copy
Ask AI
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
Copy
Ask AI
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
Copy
Ask AI
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
Copy
Ask AI
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
Copy
Ask AI
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 usingautoExecute: true — request authorization and execute the payment in one API call.
Copy
Ask AI
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
CallGET /api/sdk/setup on startup to understand your agent’s full configuration — wallets, policies, counterparties, and capabilities.
Copy
Ask AI
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.Copy
Ask AI
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.Copy
Ask AI
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
Copy
Ask AI
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)
Copy
Ask AI
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<string, {
amount: number;
recipient: string;
resolve: (value: any) => 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:Copy
Ask AI
{
"mcpServers": {
"conto": {
"command": "npx",
"args": ["conto-mcp-server"],
"env": {
"CONTO_API_KEY": "conto_agent_xxx..."
}
}
}
}
“Pay $50 to 0x123… for API credits”