Building Agents
This guide walks you through building an AI agent that can autonomously make purchases using AutopayOS. You'll learn how to request permissions, interact with merchants, and execute payments.
Overview
A shopping agent follows this flow:
Diagram
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Receive │────▶│ Request │────▶│ Shop & │────▶│ Execute │ │ Goal │ │ Permission │ │ Build Cart │ │ Payment │ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
Prerequisites
- Node.js 18+ or Python 3.9+
- AutopayOS API key
- Understanding of DIDs and VCs (see Authentication)
Project setup
TypeScript
Bash
mkdir my-shopping-agent
cd my-shopping-agent
npm init -y
npm install @autopayos/sdk typescript ts-nodeCreate tsconfig.json:
JSON
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"outDir": "./dist"
}
}Python
Bash
mkdir my-shopping-agent
cd my-shopping-agent
python -m venv venv
source venv/bin/activate
pip install autopayosBasic agent structure
<tabs> <tab title="TypeScript">TypeScript
// agent.ts
import { AutopayosClient } from '@autopayos/sdk';
interface ShoppingGoal {
description: string;
maxBudget: number;
merchant?: string;
}
class ShoppingAgent {
private client: AutopayosClient;
private agentDid: string;
private principalDid: string;
constructor(
apiKey: string,
agentDid: string,
principalDid: string
) {
this.client = new AutopayosClient({
baseUrl: 'https://api.autopayos.com',
apiKey,
});
this.agentDid = agentDid;
this.principalDid = principalDid;
}
async shop(goal: ShoppingGoal): Promise<void> {
console.log(`🛒 Shopping for: ${goal.description}`);
console.log(`💰 Budget: $${goal.maxBudget}`);
// Step 1: Request permission
const permission = await this.requestPermission(goal);
if (!permission.allowed) {
throw new Error(`Permission denied: ${permission.reasonCodes}`);
}
// Step 2: Find products and build cart
const cart = await this.buildCart(goal);
// Step 3: Verify cart
const verification = await this.verifyCart(permission.intentVc, cart);
if (!verification.allowed) {
throw new Error(`Cart rejected: ${verification.reasonCodes}`);
}
// Step 4: Execute payment
const payment = await this.executePayment(verification.approvalToken);
console.log('Purchase complete!');
console.log(`Order ID: ${payment.pmVc.credentialSubject.mandateId}`);
}
private async requestPermission(goal: ShoppingGoal) {
console.log('Requesting permission...');
return await this.client.requestPermission({
agentDid: this.agentDid,
principalDid: this.principalDid,
amount: goal.maxBudget,
currency: 'USD',
merchantDomain: goal.merchant,
});
}
private async buildCart(goal: ShoppingGoal) {
console.log('🔍 Finding products...');
// In a real agent, you would:
// 1. Browse merchant website
// 2. Search for products matching the goal
// 3. Compare prices and reviews
// 4. Select optimal products
// For demo, return a mock cart
return {
merchant: goal.merchant || 'amazon.com',
items: [
{ name: 'USB-C Cable 6ft', price: 12.99, quantity: 2 },
{ name: 'Phone Stand', price: 9.99, quantity: 1 },
],
total: 35.97,
currency: 'USD',
};
}
private async verifyCart(intentVc: any, cart: any) {
console.log('Verifying cart...');
// Convert cart to VC format
const cartVc = {
type: ['VerifiableCredential', 'CartMandate'],
credentialSubject: cart,
};
return await this.client.verifyCart({
intentVc,
cartVc,
});
}
private async executePayment(approvalToken: string) {
console.log('Executing payment...');
return await this.client.executePayment({
approvalToken,
});
}
}
// Usage
async function main() {
const agent = new ShoppingAgent(
process.env.AUTOPAYOS_API_KEY!,
'did:key:z6MkAgentDid...', // Your agent's DID
'did:key:z6MkUserDid...', // User who authorized the agent
);
await agent.shop({
description: 'Buy USB cables for new laptop',
maxBudget: 50.00,
merchant: 'amazon.com',
});
}
main().catch(console.error);Python
# agent.py
from autopayos import AutopayosClient
from dataclasses import dataclass
from typing import Optional, List, Dict, Any
@dataclass
class ShoppingGoal:
description: str
max_budget: float
merchant: Optional[str] = None
@dataclass
class CartItem:
name: str
price: float
quantity: int
class ShoppingAgent:
def __init__(
self,
api_key: str,
agent_did: str,
principal_did: str
):
self.client = AutopayosClient(
base_url="https://api.autopayos.com",
api_key=api_key
)
self.agent_did = agent_did
self.principal_did = principal_did
async def shop(self, goal: ShoppingGoal) -> Dict[str, Any]:
print(f"🛒 Shopping for: {goal.description}")
print(f"💰 Budget: ${goal.max_budget}")
# Step 1: Request permission
permission = await self.request_permission(goal)
if not permission.allowed:
raise Exception(f"Permission denied: {permission.reason_codes}")
# Step 2: Find products and build cart
cart = await self.build_cart(goal)
# Step 3: Verify cart
verification = await self.verify_cart(permission.intent_vc, cart)
if not verification.allowed:
raise Exception(f"Cart rejected: {verification.reason_codes}")
# Step 4: Execute payment
payment = await self.execute_payment(verification.approval_token)
print("Purchase complete!")
print(f"Order ID: {payment.pm_vc['credentialSubject']['mandateId']}")
return payment
async def request_permission(self, goal: ShoppingGoal):
print("Requesting permission...")
return await self.client.request_permission(
agent_did=self.agent_did,
principal_did=self.principal_did,
amount=goal.max_budget,
currency="USD",
merchant_domain=goal.merchant
)
async def build_cart(self, goal: ShoppingGoal) -> Dict[str, Any]:
print("🔍 Finding products...")
# In a real agent, browse and search
return {
"merchant": goal.merchant or "amazon.com",
"items": [
{"name": "USB-C Cable 6ft", "price": 12.99, "quantity": 2},
{"name": "Phone Stand", "price": 9.99, "quantity": 1}
],
"total": 35.97,
"currency": "USD"
}
async def verify_cart(self, intent_vc, cart):
print("Verifying cart...")
cart_vc = {
"type": ["VerifiableCredential", "CartMandate"],
"credentialSubject": cart
}
return await self.client.verify_cart(
intent_vc=intent_vc,
cart_vc=cart_vc
)
async def execute_payment(self, approval_token: str):
print("Executing payment...")
return await self.client.execute_payment(
approval_token=approval_token
)
# Usage
import asyncio
import os
async def main():
agent = ShoppingAgent(
api_key=os.environ["AUTOPAYOS_API_KEY"],
agent_did="did:key:z6MkAgentDid...",
principal_did="did:key:z6MkUserDid..."
)
await agent.shop(ShoppingGoal(
description="Buy USB cables for new laptop",
max_budget=50.00,
merchant="amazon.com"
))
asyncio.run(main())Adding LLM decision making
Enhance your agent with LLM-powered product selection:
TypeScript
import OpenAI from 'openai';
class SmartShoppingAgent extends ShoppingAgent {
private openai: OpenAI;
constructor(apiKey: string, openaiKey: string, agentDid: string, principalDid: string) {
super(apiKey, agentDid, principalDid);
this.openai = new OpenAI({ apiKey: openaiKey });
}
private async selectProducts(goal: ShoppingGoal, products: Product[]): Promise<Product[]> {
const response = await this.openai.chat.completions.create({
model: 'gpt-4',
messages: [
{
role: 'system',
content: `You are a shopping assistant. Select products that best match the user's goal within budget. Return a JSON array of product IDs.`
},
{
role: 'user',
content: `
Goal: ${goal.description}
Budget: $${goal.maxBudget}
Available products:
${JSON.stringify(products, null, 2)}
Select the best products and return their IDs as a JSON array.
`
}
],
response_format: { type: 'json_object' },
});
const selection = JSON.parse(response.choices[0].message.content!);
return products.filter(p => selection.productIds.includes(p.id));
}
}Handling Human Presence
When Human Presence (HP) is required, prompt the user:
TypeScript
async shop(goal: ShoppingGoal): Promise<void> {
const permission = await this.requestPermission(goal);
// Check if HP is required
if (permission.hpRequired) {
console.log('🔐 Human verification required');
// Get HP proof from user (WebAuthn)
const hpProof = await this.getHumanPresenceProof(permission.hpChallenge);
// Include HP proof in cart verification
const verification = await this.client.verifyCart({
intentVc: permission.intentVc,
cartVc: cart,
hpProof: hpProof,
});
}
}
private async getHumanPresenceProof(challenge: HpChallenge) {
// In a browser context:
const credential = await navigator.credentials.get({
publicKey: {
challenge: Buffer.from(challenge.challengeId),
userVerification: 'required',
},
});
return {
type: 'WebAuthn',
challengeId: challenge.challengeId,
credential: credential,
};
}Error handling
Build robust error handling:
TypeScript
async shop(goal: ShoppingGoal): Promise<void> {
try {
// ... shopping logic
} catch (error) {
await this.handleError(error, goal);
}
}
private async handleError(error: any, goal: ShoppingGoal) {
const code = error.code || error.message;
switch (code) {
case 'AMOUNT_OVER_CAP':
console.log('Budget exceeds policy limit. Trying with lower amount...');
await this.shop({ ...goal, maxBudget: goal.maxBudget * 0.5 });
break;
case 'MERCHANT_NOT_ALLOWED':
console.log('Merchant not allowed. Trying alternative...');
const alternatives = await this.findAlternativeMerchants(goal);
if (alternatives.length > 0) {
await this.shop({ ...goal, merchant: alternatives[0] });
}
break;
case 'VELOCITY_EXCEEDED':
console.log('⏳ Too many transactions. Will retry later.');
// Schedule retry
break;
case 'APPROVAL_TOKEN_EXPIRED':
console.log('🔄 Token expired. Restarting flow...');
await this.shop(goal);
break;
default:
console.error('Unhandled error:', error);
throw error;
}
}Registering your agent
Register your agent with AutopayOS:
TypeScript
async function registerAgent() {
const client = new AutopayosClient({
baseUrl: 'https://api.autopayos.com',
apiKey: process.env.AUTOPAYOS_API_KEY,
});
const agent = await client.registerAgent({
name: 'My Shopping Assistant',
description: 'AI-powered shopping agent for daily purchases',
capabilities: ['browse', 'purchase', 'compare'],
publicKey: myPublicKey, // Ed25519 public key
});
console.log('Agent DID:', agent.did);
console.log('Status:', agent.status);
return agent;
}Agent lifecycle
Diagram
┌──────────────┐
│ PENDING │ Registration submitted
└──────┬───────┘
│
▼
┌──────────────┐
│ ACTIVE │ Ready to transact
└──────┬───────┘
│
├──────────────────────────────────┐
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ REVOKED │ │ SUSPENDED │
│ │ │ │
│ Permanently │ │ Temporarily │
│ disabled │ │ disabled │
└──────────────┘ └──────────────┘Monitoring agent activity
Query your agent's transaction history:
TypeScript
const activity = await client.getAgentActivity(agentDid, {
from: '2025-12-01',
to: '2025-12-17',
});
console.log('Total transactions:', activity.count);
console.log('Total spent:', activity.totalAmount);
console.log('Success rate:', activity.successRate);
for (const txn of activity.transactions) {
console.log(`${txn.merchant}: $${txn.amount} - ${txn.status}`);
}Testing your agent
Use sandbox mode for testing:
TypeScript
const client = new AutopayosClient({
baseUrl: 'https://sandbox-api.autopayos.com',
apiKey: 'ak_test_...',
});
// Use test DIDs
const testAgentDid = 'did:key:z6MkTest123...';
const testUserDid = 'did:key:z6MkTestUser...';
// Test different scenarios
await testPurchaseSuccess();
await testPurchaseDenied();
await testHumanPresenceRequired();Best practices
1. Request minimum permissions
Only ask for what you need:
TypeScript
// Good: Specific request
await client.requestPermission({
amount: 25.00,
merchantDomain: 'amazon.com',
// ...
});
// Bad: Overly broad
await client.requestPermission({
amount: 10000.00,
// No merchant restriction
// ...
});2. Handle all error cases
TypeScript
const permission = await client.requestPermission({ ... });
if (!permission.allowed) {
// Log for debugging
console.log('Denied with codes:', permission.reasonCodes);
// Inform user appropriately
return {
success: false,
message: this.getUserFriendlyMessage(permission.reasonCodes),
};
}3. Respect rate limits
TypeScript
import { RateLimiter } from 'limiter';
const limiter = new RateLimiter({
tokensPerInterval: 10,
interval: 'minute',
});
async function makeRequest() {
await limiter.removeTokens(1);
return await client.requestPermission({ ... });
}4. Log evidence IDs
Always log transaction IDs for audit:
TypeScript
const payment = await client.executePayment({ approvalToken });
console.log('Transaction completed', {
mandateId: payment.pmVc.credentialSubject.mandateId,
amount: payment.pmVc.credentialSubject.amount,
timestamp: new Date().toISOString(),
});
// Store for later reference
await db.transactions.create({
mandateId: payment.pmVc.credentialSubject.mandateId,
// ...
});Example: Complete agent
See the full example in our GitHub repo:
Bash
git clone https://github.com/autopayos/examples
cd examples/simple-agent
npm install
npm run devNext steps
- A2A Protocol — Agent-to-agent communication
- Human Presence — WebAuthn integration
- Testing guide — Test your agent
- SDK Reference — Complete API