Skip to content

Latest commit

 

History

History
1440 lines (1143 loc) · 38.8 KB

File metadata and controls

1440 lines (1143 loc) · 38.8 KB

REST API Integration Guide - AgentGatePay Python SDK

Complete guide to using the AgentGatePay Python SDK for autonomous agent payments.

This guide covers the REST API approach using the official agentgatepay-sdk Python package. For MCP tools approach, see MCP_INTEGRATION.md.


Table of Contents

  1. Why Use the SDK?
  2. Installation
  3. Quick Start
  4. SDK Modules
  5. Complete Workflows
  6. Authentication
  7. Error Handling
  8. Best Practices
  9. Troubleshooting

Why Use the SDK?

The AgentGatePay Python SDK provides a Pythonic, type-safe interface to the REST API.

Benefits:

  • Type Safety: Full type hints for IDE autocomplete
  • Object-Oriented: Clean, intuitive API design
  • Error Handling: Custom exceptions for better debugging
  • Web3 Helpers: Built-in blockchain utilities
  • Automatic Retries: Smart retry logic for transient errors
  • Production-Ready: Tested with 95%+ coverage

SDK vs MCP Comparison:

Feature Python SDK MCP Tools
Installation pip install agentgatepay-sdk No dependencies
API Style Object-oriented JSON-RPC 2.0
Type Hints ✅ Full Python types ❌ JSON only
IDE Support ✅ Autocomplete ⚠️ Limited
Framework Python-specific Universal
Best For Python apps, type safety Multi-framework, AI agents

When to use SDK:

  • Building Python-only applications
  • Want IDE autocomplete and type checking
  • Prefer object-oriented interfaces
  • Need advanced Web3 utilities

When to use MCP:

  • Framework-agnostic integration
  • Multi-language environment
  • AI agent tool discovery
  • Claude Desktop, OpenAI Agent Builder

Installation

Requirements

  • Python 3.12 or higher
  • pip package manager

Install SDK

pip install agentgatepay-sdk>=1.1.6

Verify Installation

import agentgatepay_sdk
print(f"SDK Version: {agentgatepay_sdk.__version__}")  # Should be >= 1.1.6

Optional Dependencies

For blockchain integration (if not using SDK's Web3 helpers):

pip install web3>=6.0.0 eth-account>=0.9.0

For LangChain integration:

pip install langchain>=0.1.0 langchain-openai>=0.0.5

Quick Start

1. Configure Environment

Create .env file:

# AgentGatePay
AGENTPAY_API_URL=https://api.agentgatepay.com
BUYER_API_KEY=pk_live_YOUR_BUYER_KEY_HERE
SELLER_API_KEY=pk_live_YOUR_SELLER_KEY_HERE

# Blockchain (Base network)
BASE_RPC_URL=https://mainnet.base.org
BUYER_PRIVATE_KEY=0xYOUR_64_CHAR_PRIVATE_KEY
BUYER_WALLET=0xYOUR_BUYER_WALLET_ADDRESS
SELLER_WALLET=0xYOUR_SELLER_WALLET_ADDRESS

# OpenAI (for LangChain examples)
OPENAI_API_KEY=sk-YOUR_OPENAI_KEY

2. Initialize SDK

from agentgatepay_sdk import AgentGatePay
from dotenv import load_dotenv
import os

# Load environment variables
load_dotenv()

# Initialize SDK
agentpay = AgentGatePay(
    api_url=os.getenv('AGENTPAY_API_URL'),
    api_key=os.getenv('BUYER_API_KEY')
)

# Test connection
health = agentpay.health()
if health['status'] == 'healthy':
    print(f"✅ Connected to AgentGatePay ({health['endpoint']} endpoint)")
    print(f"   All {len(health['components'])} components healthy")

3. First Payment Flow (3-Step Process)

Complete payment flow: Issue Mandate → Sign Transactions → Submit to Gateway

from web3 import Web3
from eth_account import Account
import requests
import json
import base64

# Step 1: Issue mandate ($100 budget for 7 days)
mandate = agentpay.mandates.issue(
    subject="buyer-agent-12345",
    budget=100.0,
    scope="resource.read,payment.execute",
    ttl_minutes=10080  # 7 days (168 hours * 60)
)
print(f"✅ Mandate issued: {mandate['mandate_id']}")
print(f"   Budget: ${mandate['budget_usd']}")

# Step 1b: Verify mandate and get live budget
verification = agentpay.mandates.verify(mandate['mandate_token'])
print(f"✅ Mandate valid: ${verification['budget_remaining']} remaining")

# Step 1c: Fetch commission configuration dynamically
commission_response = requests.get(
    f"{os.getenv('AGENTPAY_API_URL')}/v1/config/commission",
    headers={"x-api-key": os.getenv('BUYER_API_KEY')}
)
commission_config = commission_response.json()
commission_address = commission_config['commission_address']
commission_rate = commission_config.get('commission_rate', 0.005)
print(f"✅ Commission config: {commission_rate*100}% to {commission_address[:10]}...")

# Step 2: Sign TWO blockchain transactions (merchant + commission)
web3 = Web3(Web3.HTTPProvider(os.getenv('BASE_RPC_URL')))
account = Account.from_key(os.getenv('BUYER_PRIVATE_KEY'))

USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
USDC_ABI = [{"constant": False, "inputs": [{"name": "_to", "type": "address"}, {"name": "_value", "type": "uint256"}], "name": "transfer", "outputs": [{"name": "", "type": "bool"}], "type": "function"}]
usdc_contract = web3.eth.contract(address=USDC_ADDRESS, abi=USDC_ABI)

amount_usd = 0.01
merchant_amount = int(amount_usd * (1 - commission_rate) * 10**6)  # 99.5%
commission_amount = int(amount_usd * commission_rate * 10**6)      # 0.5%

# TX 1: Merchant payment
tx1 = usdc_contract.functions.transfer(
    os.getenv('SELLER_WALLET'), merchant_amount
).build_transaction({
    'from': account.address,
    'nonce': web3.eth.get_transaction_count(account.address),
    'gas': 100000,
    'gasPrice': web3.eth.gas_price
})
signed_tx1 = web3.eth.account.sign_transaction(tx1, os.getenv('BUYER_PRIVATE_KEY'))
tx_hash1 = web3.eth.send_raw_transaction(signed_tx1.raw_transaction)
receipt1 = web3.eth.wait_for_transaction_receipt(tx_hash1)
print(f"✅ TX 1/2 confirmed: {receipt1.transactionHash.hex()}")

# TX 2: Commission payment
tx2 = usdc_contract.functions.transfer(
    commission_address, commission_amount
).build_transaction({
    'from': account.address,
    'nonce': web3.eth.get_transaction_count(account.address),
    'gas': 100000,
    'gasPrice': web3.eth.gas_price
})
signed_tx2 = web3.eth.account.sign_transaction(tx2, os.getenv('BUYER_PRIVATE_KEY'))
tx_hash2 = web3.eth.send_raw_transaction(signed_tx2.raw_transaction)
receipt2 = web3.eth.wait_for_transaction_receipt(tx_hash2)
print(f"✅ TX 2/2 confirmed: {receipt2.transactionHash.hex()}")

# Step 3: Submit payment proof to AgentGatePay gateway
payment_payload = {
    "scheme": "eip3009",
    "tx_hash": receipt1.transactionHash.hex(),
    "tx_hash_commission": receipt2.transactionHash.hex()
}
payment_b64 = base64.b64encode(json.dumps(payment_payload).encode()).decode()

payment_response = requests.get(
    f"{os.getenv('AGENTPAY_API_URL')}/x402/resource?chain=base&token=USDC&price_usd={amount_usd}",
    headers={
        "x-api-key": os.getenv('BUYER_API_KEY'),
        "x-mandate": mandate['mandate_token'],
        "x-payment": payment_b64
    }
)
payment_result = payment_response.json()
print(f"✅ Payment verified by gateway")
print(f"   Charge ID: {payment_result.get('charge_id')}")

# Fetch updated budget
updated_verification = agentpay.mandates.verify(mandate['mandate_token'])
print(f"   Budget remaining: ${updated_verification['budget_remaining']}")

Expected Output:

✅ Connected to AgentGatePay v1.1.6
✅ Mandate issued: mandate_abc123
   Budget: $100.0
✅ Mandate valid: $100.0 remaining
✅ Commission config: 0.5% to 0x742d35Cc...
✅ TX 1/2 confirmed: 0xabc123...
✅ TX 2/2 confirmed: 0xdef456...
✅ Payment verified by gateway
   Charge ID: charge_xyz789
   Budget remaining: $99.99

Key Features:

  • ✅ Dynamic commission fetching from API
  • ✅ Live budget tracking via mandate verification
  • ✅ Two-transaction model (merchant + commission)
  • ✅ Gateway verifies both transactions on-chain
  • ✅ Automatic budget updates

SDK Modules

The SDK is organized into 6 modules, each handling a specific domain. The health() method is available directly on the main client object.

Module 1: Authentication (auth)

User account management and authentication.

# Create new account
user = agentpay.auth.signup(
    email="agent@example.com",
    password="SecurePass123!",
    user_type="agent"  # or "merchant" or "both"
)
api_key = user['api_key']  # Save this! Shown only once
print(f"✅ User created: {user['user']['user_id']}")

# Get current user info
user_info = agentpay.auth.me()
print(f"Email: {user_info['email']}")
print(f"Type: {user_info['user_type']}")
print(f"Wallets: {user_info['wallets']}")

# Add wallet address
wallet = agentpay.auth.add_wallet("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0")
print(f"✅ Wallet added: {wallet['wallet_address']}")

# Create new API key
new_key = agentpay.auth.create_api_key(name="Production Key")
print(f"✅ New API key: {new_key['api_key']}")  # Save immediately!

# List all API keys
keys = agentpay.auth.list_api_keys()
for key in keys['keys']:
    status = "🟢" if key['is_active'] else "🔴"
    print(f"{status} {key['name']}: {key['key_id']}")

# Revoke API key
agentpay.auth.revoke_api_key(key_id="key_old123")
print(f"✅ API key revoked")

Type Signatures:

def signup(email: str, password: str, user_type: str) -> Dict[str, Any]
def me() -> Dict[str, Any]
def add_wallet(wallet_address: str) -> Dict[str, Any]
def create_api_key(name: Optional[str] = None) -> Dict[str, Any]
def list_api_keys() -> Dict[str, List[Dict[str, Any]]]
def revoke_api_key(key_id: str) -> Dict[str, Any]

Module 2: Mandates (mandates)

AP2 mandate issuance and verification.

# Issue mandate
mandate = agentpay.mandates.issue(
    subject="agent-12345",
    budget=100.0,
    scope="resource.read,payment.execute",
    ttl_minutes=10080  # 7 days (168 hours * 60, default)
)

mandate_token = mandate['mandate_token']  # Use for payments
print(f"✅ Mandate ID: {mandate['mandate_id']}")
print(f"   Budget: ${mandate['budget_usd']}")
print(f"   Expires: {mandate['expires_at']}")

# Verify mandate
verification = agentpay.mandates.verify(mandate_token)

if verification['valid']:
    print(f"✅ Mandate valid")
    print(f"   Budget remaining: ${verification['budget_remaining']}")
    print(f"   Scope: {verification['scope']}")
else:
    print(f"❌ Mandate invalid: {verification.get('error')}")

Type Signatures:

def issue(
    subject: str,
    budget: float,
    scope: str,
    ttl_minutes: int = 10080  # 7 days default
) -> Dict[str, Any]

def verify(mandate_token: str) -> Dict[str, Any]

Mandate Scopes:

  • resource.read - Read resource metadata
  • payment.execute - Execute payments
  • resource.write - Write resources (future)

Common Patterns:

# Check budget before payment
verification = agentpay.mandates.verify(mandate_token)
if verification['budget_remaining'] < payment_amount:
    raise ValueError("Insufficient budget")

# Issue mandate with custom TTL
mandate = agentpay.mandates.issue(
    subject="agent-12345",
    budget=50.0,
    scope="resource.read,payment.execute",
    ttl_minutes=1440  # 1 day only (24 hours * 60)
)

Module 3: Payments (payments)

Payment submission, verification, and history.

# Submit payment (after blockchain transaction)
payment = agentpay.payments.submit(
    mandate_token=mandate_token,
    amount_usd=0.01,
    receiver_address="0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
    tx_hash="0xabc123def456...",
    chain="base",  # or "ethereum", "polygon", "arbitrum"
    resource_id="research-paper-2025"  # optional
)

print(f"✅ Payment verified: {payment['charge_id']}")
print(f"   Merchant TX: {payment['merchant_tx_hash']}")
print(f"   Commission TX: {payment['commission_tx_hash']}")
print(f"   Budget remaining: ${payment['budget_remaining']}")

# Verify payment (public, no auth)
verification = agentpay.payments.verify(
    tx_hash="0xabc123def456...",
    chain="base"
)

if verification['verified']:
    print(f"✅ Payment verified on blockchain")
    print(f"   Amount: ${verification['amount_usd']}")
    print(f"   Block: {verification['block_number']}")
    print(f"   From: {verification['from_address']}")
    print(f"   To: {verification['to_address']}")

# Quick status check
status = agentpay.payments.status("0xabc123def456...")
print(f"Status: {status['status']}")  # pending | confirmed | failed

# List payment history (merchant view)
payments = agentpay.payments.list(
    limit=50,
    start_time=1700000000,  # Unix timestamp
    end_time=1700100000
)

print(f"Total payments: {payments['total_count']}")
for payment in payments['payments']:
    print(f"  ${payment['amount_usd']} - {payment['resource_id']} - {payment['status']}")

Type Signatures:

def submit(
    mandate_token: str,
    amount_usd: float,
    receiver_address: str,
    tx_hash: str,
    chain: str = "base",
    resource_id: Optional[str] = None
) -> Dict[str, Any]

def verify(tx_hash: str, chain: str = "base") -> Dict[str, Any]

def status(tx_hash: str) -> Dict[str, Any]

def list(
    limit: int = 50,
    start_time: Optional[int] = None,
    end_time: Optional[int] = None
) -> Dict[str, Any]

Supported Chains:

  • base - Base (default, fastest, cheapest)
  • ethereum - Ethereum mainnet
  • polygon - Polygon PoS
  • arbitrum - Arbitrum One

Supported Tokens:

  • USDC - USD Coin (6 decimals)
  • USDT - Tether USD (6 decimals)
  • DAI - Dai Stablecoin (18 decimals)

Module 4: Webhooks (webhooks)

Payment notification webhooks for merchants.

# Configure webhook
webhook = agentpay.webhooks.configure(
    url="https://your-server.com/webhook",
    events=["payment.completed", "payment.failed", "mandate.expired"],
    secret="your_webhook_secret_123"  # For HMAC signature verification
)

print(f"✅ Webhook configured: {webhook['webhook_id']}")
print(f"   URL: {webhook['url']}")
print(f"   Events: {', '.join(webhook['events'])}")

# List all webhooks
webhooks = agentpay.webhooks.list()
for wh in webhooks['webhooks']:
    print(f"Webhook {wh['webhook_id']}: {wh['url']}")

# Test webhook delivery
test_result = agentpay.webhooks.test(webhook['webhook_id'])
if test_result['success']:
    print(f"✅ Webhook test successful")
else:
    print(f"❌ Webhook test failed: {test_result['error']}")

# Delete webhook
agentpay.webhooks.delete(webhook['webhook_id'])
print(f"✅ Webhook deleted")

Webhook Payload Example:

{
  "event": "payment.completed",
  "timestamp": 1700000000,
  "data": {
    "charge_id": "charge_abc123",
    "amount_usd": 0.01,
    "merchant_amount": 0.00995,
    "commission_amount": 0.00005,
    "receiver_address": "0x742d35...",
    "payer": "0x123abc...",
    "tx_hash": "0xabc123...",
    "resource_id": "research-paper-2025",
    "paid_at": 1700000000
  },
  "signature": "sha256=abc123def456..."
}

Verify Webhook Signature:

import hmac
import hashlib

def verify_webhook_signature(payload: str, signature: str, secret: str) -> bool:
    """Verify webhook HMAC signature"""
    expected = hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)

# In your webhook handler
@app.route('/webhook', methods=['POST'])
def webhook_handler():
    payload = request.get_data(as_text=True)
    signature = request.headers.get('X-Webhook-Signature')

    if not verify_webhook_signature(payload, signature, WEBHOOK_SECRET):
        return jsonify({"error": "Invalid signature"}), 401

    event = request.json
    if event['event'] == 'payment.completed':
        handle_payment_completed(event['data'])

    return jsonify({"status": "ok"}), 200

Type Signatures:

def configure(
    url: str,
    events: List[str],
    secret: Optional[str] = None
) -> Dict[str, Any]

def list() -> Dict[str, List[Dict[str, Any]]]

def test(webhook_id: str) -> Dict[str, Any]

def delete(webhook_id: str) -> Dict[str, Any]

Module 5: Analytics (analytics)

Revenue and spending analytics.

# Get user analytics (agent or merchant)
analytics = agentpay.analytics.me(
    start_time=1700000000,  # Optional: Unix timestamp
    end_time=1700100000
)

if analytics['user_type'] == 'agent':
    print(f"Total spent: ${analytics['total_spent_usd']}")
    print(f"Transactions: {analytics['transaction_count']}")
    print(f"Active mandates: {analytics['active_mandates']}")
    print(f"Budget remaining: ${analytics['budget_remaining']}")

    print(f"\nTop merchants:")
    for merchant in analytics['top_merchants']:
        print(f"  {merchant['address']}: ${merchant['amount']}")

elif analytics['user_type'] == 'merchant':
    print(f"Total revenue: ${analytics['total_revenue_usd']}")
    print(f"Net revenue: ${analytics['net_revenue_usd']}")
    print(f"Commission paid: ${analytics['commission_paid_usd']}")
    print(f"Transactions: {analytics['transaction_count']}")
    print(f"Unique payers: {analytics['unique_payers']}")

# Get public platform analytics (no auth required)
public_analytics = agentpay.analytics.public()
print(f"\nPlatform Stats:")
print(f"Total volume: ${public_analytics['total_volume_usd']}")
print(f"Total transactions: {public_analytics['transaction_count']}")
print(f"Active users: {public_analytics['active_users']}")

Type Signatures:

def me(
    start_time: Optional[int] = None,
    end_time: Optional[int] = None
) -> Dict[str, Any]

def public() -> Dict[str, Any]

Module 6: Audit (audit)

Comprehensive audit logging and compliance.

# Get audit logs
logs = agentpay.audit.logs(
    event_type="payment_completed",  # Optional filter
    limit=50,
    start_time=1700000000,
    end_time=1700100000
)

print(f"Total logs: {logs['total_count']}")
for log in logs['logs']:
    print(f"[{log['timestamp']}] {log['event_type']}: {log['description']}")

# Get audit statistics
stats = agentpay.audit.stats()
print(f"\nAudit Stats:")
print(f"Total events: {stats['total_events']}")
print(f"Event types: {', '.join(stats['event_types'])}")
print(f"Time range: {stats['oldest_event']} - {stats['newest_event']}")

# Get logs by transaction
tx_logs = agentpay.audit.by_transaction("0xabc123def456...")
print(f"\nTransaction logs: {len(tx_logs['logs'])} events")
for log in tx_logs['logs']:
    print(f"  {log['event_type']}: {log['description']}")

Common Event Types:

  • user_signup - New user registration
  • mandate_issued - Mandate created
  • mandate_verified - Mandate validation
  • payment_initiated - Payment started
  • payment_completed - Payment successful
  • payment_failed - Payment failed
  • webhook_delivered - Webhook sent
  • api_key_created - New API key generated
  • api_key_revoked - API key revoked
  • audit_log_access - Audit log viewed

Type Signatures:

def logs(
    event_type: Optional[str] = None,
    limit: int = 50,
    start_time: Optional[int] = None,
    end_time: Optional[int] = None
) -> Dict[str, Any]

def stats() -> Dict[str, Any]

def by_transaction(tx_hash: str) -> Dict[str, Any]

Health Check

System health monitoring with 7-component validation.

The SDK provides a health() method (on the main client, not a module) that validates all gateway components.

# Check system health
health = agentpay.health()

if health['status'] == 'healthy':
    print(f"✅ System healthy ({health['endpoint']} endpoint)")
    print(f"   Timestamp: {health['timestamp']}")
    print(f"\n   Components:")
    for component, status in health['components'].items():
        icon = "✅" if status == "ok" else "❌"
        print(f"   {icon} {component}: {status}")
else:
    print(f"⚠️ System degraded")
    # Check which components failed
    failed = [c for c, s in health['components'].items() if s != 'ok']
    print(f"   Failed components: {', '.join(failed)}")

Response Format:

{
  "status": "healthy",
  "endpoint": "api",
  "timestamp": 1764703713,
  "components": {
    "gateway": "ok",
    "payments": "ok",
    "aif": "ok",
    "mandates": "ok",
    "mcp": "ok",
    "api": "ok",
    "audit_logs": "ok"
  }
}

Component Descriptions:

  • gateway: Core gateway services
  • payments: Payment processing system
  • aif: Security and rate limiting
  • mandates: Authorization system
  • mcp: Tool integration layer
  • api: REST API availability
  • audit_logs: Logging and compliance

Type Signature:

def health() -> Dict[str, Any]

Note: The health() method is on the main AgentGatePay client object, NOT a separate module. There is no system module in the SDK.


Complete Workflows

Workflow 1: Autonomous Buyer Agent

Full payment flow from account creation to resource access.

from agentgatepay_sdk import AgentGatePay
from web3 import Web3
from eth_account import Account
import requests
import os
from dotenv import load_dotenv

load_dotenv()

# Step 1: Initialize SDK (first time: no API key yet)
agentpay_anon = AgentGatePay(api_url=os.getenv('AGENTPAY_API_URL'))

# Step 2: Create buyer account
user = agentpay_anon.auth.signup(
    email="buyer_agent@example.com",
    password="SecurePass123!",
    user_type="agent"
)
api_key = user['api_key']  # SAVE THIS!
print(f"✅ Account created: {user['user']['user_id']}")

# Step 3: Re-initialize SDK with API key (5x rate limit)
agentpay = AgentGatePay(
    api_url=os.getenv('AGENTPAY_API_URL'),
    api_key=api_key
)

# Step 4: Add wallet address
wallet = agentpay.auth.add_wallet(os.getenv('BUYER_WALLET'))
print(f"✅ Wallet added: {wallet['wallet_address']}")

# Step 5: Issue mandate ($100 budget, 7 days)
mandate = agentpay.mandates.issue(
    subject="buyer-agent-12345",
    budget=100.0,
    scope="resource.read,payment.execute",
    ttl_minutes=10080  # 168 hours * 60
)
mandate_token = mandate['mandate_token']
print(f"✅ Mandate issued: {mandate['mandate_id']}")

# Step 6: Verify mandate
verification = agentpay.mandates.verify(mandate_token)
print(f"✅ Budget: ${verification['budget_remaining']}")

# Step 7: Discover resources from seller
seller_url = "http://localhost:8000"
catalog_response = requests.get(f"{seller_url}/catalog")
catalog = catalog_response.json()['catalog']
resource = catalog[0]
print(f"✅ Found resource: {resource['id']} (${resource['price_usd']})")

# Step 8: Request resource (expect HTTP 402)
resource_response = requests.get(
    f"{seller_url}/resource/{resource['id']}",
    headers={
        "x-agent-id": "buyer-agent-12345",
        "x-mandate": mandate_token
    }
)
assert resource_response.status_code == 402
payment_info = resource_response.json()
print(f"✅ 402 Payment Required: ${payment_info['amount_usd']}")

# Step 9: Sign blockchain transaction
web3 = Web3(Web3.HTTPProvider(os.getenv('BASE_RPC_URL')))
account = Account.from_key(os.getenv('BUYER_PRIVATE_KEY'))

USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
USDC_ABI = [{"constant": False, "inputs": [{"name": "_to", "type": "address"}, {"name": "_value", "type": "uint256"}], "name": "transfer", "outputs": [{"name": "", "type": "bool"}], "type": "function"}]

usdc_contract = web3.eth.contract(address=USDC_ADDRESS, abi=USDC_ABI)
merchant_amount = int(payment_info['merchant_amount'] * 10**6)

tx = usdc_contract.functions.transfer(
    payment_info['receiver_address'],
    merchant_amount
).build_transaction({
    'from': account.address,
    'nonce': web3.eth.get_transaction_count(account.address),
    'gas': 100000,
    'gasPrice': web3.eth.gas_price
})

signed_tx = web3.eth.account.sign_transaction(tx, os.getenv('BUYER_PRIVATE_KEY'))
tx_hash = web3.eth.send_raw_transaction(signed_tx.raw_transaction)
receipt = web3.eth.wait_for_transaction_receipt(tx_hash)
print(f"✅ Blockchain TX: {receipt.transactionHash.hex()}")

# Step 10: Submit payment to AgentGatePay
payment = agentpay.payments.submit(
    mandate_token=mandate_token,
    amount_usd=payment_info['amount_usd'],
    receiver_address=payment_info['receiver_address'],
    tx_hash=receipt.transactionHash.hex(),
    chain="base",
    resource_id=resource['id']
)
print(f"✅ Payment verified: {payment['charge_id']}")
print(f"   Budget remaining: ${payment['budget_remaining']}")

# Step 11: Claim resource with payment proof
resource_response = requests.get(
    f"{seller_url}/resource/{resource['id']}",
    headers={
        "x-agent-id": "buyer-agent-12345",
        "x-mandate": mandate_token,
        "x-payment": receipt.transactionHash.hex()
    }
)
assert resource_response.status_code == 200
resource_data = resource_response.json()
print(f"✅ Resource claimed: {len(resource_data['content'])} bytes")

# Step 12: View audit trail
logs = agentpay.audit.logs(limit=10)
print(f"✅ Audit logs: {logs['total_count']} events")
for log in logs['logs'][:5]:
    print(f"   [{log['event_type']}] {log['description']}")

# Step 13: Check spending analytics
analytics = agentpay.analytics.me()
print(f"✅ Analytics:")
print(f"   Total spent: ${analytics['total_spent_usd']}")
print(f"   Transactions: {analytics['transaction_count']}")
print(f"   Budget remaining: ${analytics['budget_remaining']}")

Workflow 2: Merchant Payment Verification

Merchant receives payment and verifies before delivering resource.

from agentgatepay_sdk import AgentGatePay
from flask import Flask, request, jsonify
import os
from dotenv import load_dotenv

load_dotenv()

app = Flask(__name__)

# Initialize SDK with merchant API key
agentpay = AgentGatePay(
    api_url=os.getenv('AGENTPAY_API_URL'),
    api_key=os.getenv('SELLER_API_KEY')
)

# In-memory resource catalog
CATALOG = {
    "research-paper-2025": {
        "id": "research-paper-2025",
        "price_usd": 0.01,
        "description": "AI Research Paper 2025",
        "content": "SECRET_RESEARCH_DATA_HERE"
    }
}

@app.route('/catalog', methods=['GET'])
def get_catalog():
    """Return public catalog (no auth required)"""
    catalog = [
        {
            "id": r['id'],
            "price_usd": r['price_usd'],
            "description": r['description']
        }
        for r in CATALOG.values()
    ]
    return jsonify({"catalog": catalog}), 200

@app.route('/resource/<resource_id>', methods=['GET'])
def get_resource(resource_id):
    """HTTP 402 payment flow"""

    # Check resource exists
    if resource_id not in CATALOG:
        return jsonify({"error": "Resource not found"}), 404

    resource = CATALOG[resource_id]

    # Check for payment header
    payment_tx = request.headers.get('x-payment')

    if not payment_tx:
        # No payment: Return HTTP 402
        return jsonify({
            "error": "Payment Required",
            "amount_usd": resource['price_usd'],
            "merchant_amount": resource['price_usd'] * 0.995,
            "commission_amount": resource['price_usd'] * 0.005,
            "receiver_address": os.getenv('SELLER_WALLET'),
            "resource_id": resource_id
        }), 402

    # Verify payment with AgentGatePay
    try:
        verification = agentpay.payments.verify(tx_hash=payment_tx, chain="base")

        if not verification['verified']:
            return jsonify({
                "error": "Payment verification failed",
                "reason": verification.get('reason', 'Unknown')
            }), 400

        # Check payment amount
        if verification['amount_usd'] < resource['price_usd']:
            return jsonify({
                "error": "Insufficient payment",
                "expected": resource['price_usd'],
                "received": verification['amount_usd']
            }), 400

        # Check receiver address
        if verification['to_address'].lower() != os.getenv('SELLER_WALLET').lower():
            return jsonify({
                "error": "Payment sent to wrong address",
                "expected": os.getenv('SELLER_WALLET'),
                "received": verification['to_address']
            }), 400

        # Payment verified: Deliver resource
        return jsonify({
            "resource_id": resource_id,
            "content": resource['content'],
            "tx_hash": payment_tx
        }), 200

    except Exception as e:
        return jsonify({"error": f"Verification error: {str(e)}"}), 500

@app.route('/webhook', methods=['POST'])
def webhook_handler():
    """Receive payment notifications"""
    import hmac
    import hashlib

    # Verify signature
    payload = request.get_data(as_text=True)
    signature = request.headers.get('X-Webhook-Signature')
    webhook_secret = os.getenv('WEBHOOK_SECRET')

    expected = hmac.new(
        webhook_secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(f"sha256={expected}", signature):
        return jsonify({"error": "Invalid signature"}), 401

    # Process webhook
    event = request.json
    if event['event'] == 'payment.completed':
        print(f"✅ Payment received: ${event['data']['amount_usd']}")

    return jsonify({"status": "ok"}), 200

if __name__ == '__main__':
    # Configure webhook on startup
    try:
        webhook = agentpay.webhooks.configure(
            url="https://your-server.com/webhook",
            events=["payment.completed", "payment.failed"],
            secret=os.getenv('WEBHOOK_SECRET')
        )
        print(f"✅ Webhook configured: {webhook['webhook_id']}")
    except Exception as e:
        print(f"⚠️ Webhook configuration failed: {e}")

    # Start Flask server
    app.run(host='0.0.0.0', port=8000)

Authentication

API Key Management

# Option 1: Constructor (recommended)
agentpay = AgentGatePay(
    api_url="https://api.agentgatepay.com",
    api_key="pk_live_abc123..."
)

# Option 2: Set after initialization
agentpay = AgentGatePay(api_url="https://api.agentgatepay.com")
agentpay.api_key = "pk_live_abc123..."

# Option 3: From environment variable
os.environ['AGENTPAY_API_KEY'] = "pk_live_abc123..."
agentpay = AgentGatePay(
    api_url="https://api.agentgatepay.com",
    api_key=os.getenv('AGENTPAY_API_KEY')
)

Rate Limits

  • Without API Key: 20 requests/minute
  • With API Key: 100 requests/minute (5x higher)

Check Rate Limit Headers:

# All SDK methods return response metadata
result = agentpay.mandates.issue(...)

# Access response headers via internal _last_response
if hasattr(agentpay, '_last_response'):
    headers = agentpay._last_response.headers
    print(f"Limit: {headers.get('X-RateLimit-Limit')}")
    print(f"Remaining: {headers.get('X-RateLimit-Remaining')}")
    print(f"Reset: {headers.get('X-RateLimit-Reset')}")

Error Handling

SDK Exceptions

The SDK raises custom exceptions for better error handling:

from agentgatepay_sdk.exceptions import (
    AuthenticationError,
    MandateError,
    PaymentError,
    RateLimitError,
    ValidationError,
    APIError
)

try:
    mandate = agentpay.mandates.issue(
        subject="agent-12345",
        budget=100.0,
        scope="resource.read,payment.execute"
    )
except AuthenticationError as e:
    print(f"❌ Auth failed: {e}")
    # Re-authenticate or get new API key

except MandateError as e:
    print(f"❌ Mandate error: {e}")
    # Check mandate parameters

except RateLimitError as e:
    print(f"❌ Rate limited: {e}")
    # Wait and retry (retry_after available)
    time.sleep(e.retry_after)

except ValidationError as e:
    print(f"❌ Validation error: {e}")
    # Check input parameters

except APIError as e:
    print(f"❌ API error: {e}")
    # General API error, check message

except Exception as e:
    print(f"❌ Unexpected error: {e}")
    # Unknown error, log and investigate

Retry Logic

import time

def call_with_retry(func, *args, max_retries=3, **kwargs):
    """Call SDK method with automatic retry"""

    for attempt in range(max_retries):
        try:
            return func(*args, **kwargs)

        except RateLimitError as e:
            if attempt < max_retries - 1:
                wait_time = e.retry_after or 60
                print(f"⚠️ Rate limited, waiting {wait_time}s...")
                time.sleep(wait_time)
                continue
            raise

        except APIError as e:
            if attempt < max_retries - 1 and e.status_code >= 500:
                wait_time = 2 ** attempt  # Exponential backoff
                print(f"⚠️ Server error, retrying in {wait_time}s...")
                time.sleep(wait_time)
                continue
            raise

    raise Exception(f"Failed after {max_retries} attempts")

# Usage
mandate = call_with_retry(
    agentpay.mandates.issue,
    subject="agent-12345",
    budget=100.0,
    scope="resource.read,payment.execute"
)

Best Practices

1. Secure API Key Storage

# ✅ GOOD: Environment variables
from dotenv import load_dotenv
import os

load_dotenv()
api_key = os.getenv('AGENTPAY_API_KEY')

# ❌ BAD: Hardcoded keys
api_key = "pk_live_abc123..."  # NEVER DO THIS!

2. Validate Inputs Before SDK Calls

from web3 import Web3

def validate_wallet_address(address: str) -> bool:
    """Validate Ethereum address with checksum"""
    return Web3.is_address(address)

def validate_amount(amount: float) -> bool:
    """Validate payment amount"""
    return 0 < amount <= 10000  # Max $10k per payment

# Validate before calling SDK
if not validate_wallet_address(receiver_address):
    raise ValueError("Invalid wallet address")

if not validate_amount(amount_usd):
    raise ValueError("Invalid payment amount")

payment = agentpay.payments.submit(...)

3. Check Mandate Budget Before Payment

def safe_payment(mandate_token, amount_usd, receiver_address, tx_hash):
    """Execute payment with mandate budget check"""

    # Step 1: Verify mandate
    verification = agentpay.mandates.verify(mandate_token)

    if not verification['valid']:
        raise ValueError(f"Mandate invalid: {verification.get('error')}")

    # Step 2: Check budget
    if verification['budget_remaining'] < amount_usd:
        raise ValueError(
            f"Insufficient budget: "
            f"${verification['budget_remaining']} < ${amount_usd}"
        )

    # Step 3: Submit payment
    return agentpay.payments.submit(
        mandate_token=mandate_token,
        amount_usd=amount_usd,
        receiver_address=receiver_address,
        tx_hash=tx_hash,
        chain="base"
    )

4. Use Connection Pooling for Performance

import requests
from agentgatepay_sdk import AgentGatePay

# Create session with connection pooling
session = requests.Session()
adapter = requests.adapters.HTTPAdapter(
    pool_connections=10,
    pool_maxsize=20
)
session.mount('https://', adapter)

# Pass session to SDK (if supported in future versions)
# agentpay = AgentGatePay(api_url=..., api_key=..., session=session)

5. Log SDK Operations for Debugging

import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def logged_mandate_issue(**kwargs):
    """Issue mandate with logging"""
    logger.info(f"Issuing mandate: {kwargs}")

    try:
        mandate = agentpay.mandates.issue(**kwargs)
        logger.info(f"Mandate issued: {mandate['mandate_id']}")
        return mandate

    except Exception as e:
        logger.error(f"Mandate issuance failed: {e}")
        raise

# Usage
mandate = logged_mandate_issue(
    subject="agent-12345",
    budget=100.0,
    scope="resource.read,payment.execute"
)

6. Handle Blockchain Delays

import time

def submit_payment_with_confirmation(mandate_token, amount_usd, receiver_address, web3_tx_hash):
    """Submit payment with blockchain confirmation wait"""

    # Wait for blockchain confirmation (Base: 2-5 seconds typically)
    print(f"⏳ Waiting for blockchain confirmation...")
    time.sleep(10)  # Safe wait time for Base network

    # Then submit to AgentGatePay
    return agentpay.payments.submit(
        mandate_token=mandate_token,
        amount_usd=amount_usd,
        receiver_address=receiver_address,
        tx_hash=web3_tx_hash.hex(),
        chain="base"
    )

Troubleshooting

Issue: SDK import fails

Symptom:

ModuleNotFoundError: No module named 'agentgatepay_sdk'

Solution:

# Ensure virtual environment is activated
source venv/bin/activate  # or venv\Scripts\activate on Windows

# Install/upgrade SDK
pip install --upgrade agentgatepay-sdk>=1.1.6

# Verify installation
python -c "import agentgatepay_sdk; print(agentgatepay_sdk.__version__)"

Issue: Authentication fails

Symptom:

AuthenticationError: Invalid API key

Solution:

# Check API key format (must start with pk_live_ or pk_test_)
if not api_key.startswith('pk_'):
    raise ValueError("Invalid API key format")

# Check .env file
print(f"API Key: {os.getenv('AGENTPAY_API_KEY')}")  # Should not be None

# Verify key is active
keys = agentpay.auth.list_api_keys()
active_keys = [k for k in keys['keys'] if k['is_active']]
print(f"Active keys: {len(active_keys)}")

Issue: Mandate verification fails

Symptom:

MandateError: Mandate not found or expired

Solution:

# Mandates have a TTL (default 7 days), issue a new one
mandate = agentpay.mandates.issue(
    subject="agent-12345",
    budget=100.0,
    scope="resource.read,payment.execute",
    ttl_minutes=10080  # 7 days (168 hours * 60)
)

# Save mandate token for future use
mandate_token = mandate['mandate_token']

Issue: Payment verification fails

Symptom:

PaymentError: Transaction not found on blockchain

Solution:

import time

# 1. Wait longer for blockchain confirmation
time.sleep(15)  # Base network needs 10-15 seconds

# 2. Verify correct chain
payment = agentpay.payments.verify(
    tx_hash=tx_hash,
    chain="base"  # Must match blockchain network
)

# 3. Check transaction on BaseScan
print(f"Check TX: https://basescan.org/tx/{tx_hash}")

Issue: Rate limit exceeded

Symptom:

RateLimitError: Rate limit exceeded (20 requests/minute)

Solution:

# 1. Create account to get 5x higher rate limit
user = agentpay.auth.signup(
    email="agent@example.com",
    password="SecurePass123!",
    user_type="agent"
)
api_key = user['api_key']

# 2. Use new API key (100 requests/minute)
agentpay = AgentGatePay(
    api_url="https://api.agentgatepay.com",
    api_key=api_key
)

# 3. Add retry logic with exponential backoff
from agentgatepay_sdk.exceptions import RateLimitError
import time

try:
    result = agentpay.mandates.issue(...)
except RateLimitError as e:
    time.sleep(e.retry_after or 60)
    result = agentpay.mandates.issue(...)  # Retry

See Also


Need Help?

Python SDK Resources: