Skip to main content

Official Python SDK for Paegents - Payment infrastructure for AI agents with Service Catalog and Usage Escrow

Project description

Paegents Python SDK

Official Python SDK for integrating Paegents agent payments.

Installation

pip install paegents

Initialize

import os
from paegents import PaegentsSDK

sdk = PaegentsSDK(
    api_url=os.getenv('PAEGENTS_API_URL', 'https://api.paegents.com'),
    agent_id=os.environ['PAEGENTS_AGENT_ID'],
    api_key=os.environ['PAEGENTS_API_KEY'],
    owner_jwt=os.getenv('OWNER_JWT'),  # optional: owner-only endpoints
)

Agent runtime auth is api_key + agent_id. owner_jwt is only for owner-scoped routes such as policy management, dashboard setup, or owner-managed webhooks.

Security Model

All signing happens locally — private keys never leave your environment. Escrow funds are held by an on-chain smart contract, not by Paegents. Wallet addresses are screened for sanctions compliance before activation.

Keep API keys and private keys in environment variables. Never commit them to source control.

AP2 Quick Start

from paegents import build_card_payment_method

intent = sdk.create_ap2_intent_mandate(
    policy={"max_amount": {"value": 5000}, "currency": "usd"},
    metadata={"purpose": "compute credits"},
)

cart = sdk.create_ap2_cart_mandate(
    intent_mandate_id=intent.id,
    cart={"total": 2500, "currency": "usd"},
)

payment = sdk.ap2_pay(
    intent_mandate_id=intent.id,
    cart_mandate_id=cart.id,
    payment_method=build_card_payment_method(provider='stripe'),
)

if isinstance(payment, dict) and payment.get('approval_required'):
    print('Approval required:', payment['request_id'])
else:
    print('Payment status:', payment.status)

Bilateral Escrow Usage Agreements

Bilateral escrow is a two-step process: create the agreement, then activate the on-chain escrow.

Activation is required. Without completing activation, the agreement stays at accepted / activation_status=ready. The metered proxy will reject requests and no funds are deposited on-chain.

import os, time
from paegents import (
    sign_buyer_activation_intent,
    sign_infra_fee_permit,
    build_prior_allowance_funding_authorization,
    ActivateEscrowRequestV2,
    BuyerActivationIntentRequest,
    FundingAuthorizationRequestV2,
)

# Step 1: Create the agreement (auto-accepted for self-service)
agreement = sdk.create_usage_agreement(
    seller_agent_id='seller-agent-123',
    service_id='svc_abc123',
    quantity=1000,
    unit='api_calls',
    price_per_unit_cents=10,
    buyer_wallet_address=os.environ['AGENT_WALLET_ADDRESS'],
)

# Step 2: Fetch the activation package (terms, nonces, funding options, infra fee)
package = sdk.get_activation_package(agreement.agreement_id)

# Step 3a: Sign the buyer activation intent locally (EIP-712)
intent = {
    "agreement_key": package.agreement_key,
    "platform_agreement_id_hash": package.platform_agreement_id_hash,
    "buyer": package.terms.buyer,
    "seller": package.terms.seller,
    "token": package.terms.token,
    "quantity": package.terms.quantity,
    "price_per_unit": package.terms.price_per_unit_atomic,
    "deposit_amount": package.terms.deposit_amount_atomic,
    "challenge_window_seconds": package.terms.challenge_window_seconds,
    "service_hash": package.terms.service_hash,
    "terms_hash": package.terms.terms_hash,
    "expiry": package.terms.expiry,
    "nonce": package.nonces.buyer_activation_nonce,
    "salt": package.terms.salt,
}

sig_result = sign_buyer_activation_intent(
    buyer_private_key=os.environ['AGENT_PRIVATE_KEY'],
    escrow_contract_address=package.funding.spender,
    chain_id=8453,  # Base mainnet
    intent=intent,
)

# Step 3b: Build funding authorization (prior USDC allowance)
funding = build_prior_allowance_funding_authorization(
    amount=package.terms.deposit_amount_atomic,
)

# Step 3c: Sign the separate infra fee permit when the package says a fee is pending
permit_deadline = int(time.time()) + 600
buyer_usdc_permit_nonce = int(os.environ['BUYER_USDC_PERMIT_NONCE'])  # read USDC.nonces(buyer)
infra_fee_permit = None
if package.infra_fee and not package.infra_fee.waived and package.infra_fee.status != 'paid':
    infra_fee_permit = sign_infra_fee_permit(
        buyer_private_key=os.environ['AGENT_PRIVATE_KEY'],
        spender=package.infra_fee.spender,
        value=package.infra_fee.amount_atomic,
        nonce=buyer_usdc_permit_nonce,
        deadline=permit_deadline,
        chain_id=8453,  # Base mainnet
        usdc_address=package.infra_fee.token_address,
    )

# Step 4: Submit activation to the server with both authorizations
sdk.activate_escrow(
    agreement.agreement_id,
    ActivateEscrowRequestV2(
        buyer_intent=BuyerActivationIntentRequest(**intent),
        buyer_signature=sig_result['signature'],
        funding_authorization=FundingAuthorizationRequestV2(**funding),
        buyer_infra_fee_permit_signature=infra_fee_permit['signature'] if infra_fee_permit else None,
        buyer_infra_fee_permit_deadline=permit_deadline if infra_fee_permit else None,
    ),
)

# Step 5: Poll until active (typically 10-30 seconds on Base)
while True:
    current = sdk.get_usage_agreement(agreement.agreement_id)
    if current.status == 'active':
        break
    time.sleep(3)

Optional: Metered Client

client = sdk.create_metered_client(agreement.agreement_id)

result = client.post('/generate', json={
    'prompt': 'hello world',
    'max_tokens': 150,
})

usage = client.get_usage_status()
print(usage.units_used, usage.units_remaining)

Policies and Approvals

Owner JWT is required for these endpoints.

import os

owner_jwt = os.environ['OWNER_JWT']

sdk.update_agent_policies(
    {
        'approvals': {'threshold_cents': 2000},
        'rails': {'allowed': ['card', 'stablecoin']},
        'spending': {'daily_limit_cents': 10000},
    },
    agent_id='agent-123',
    jwt_token=owner_jwt,
)

pending = sdk.list_approvals(status='pending', agent_id='agent-123', jwt_token=owner_jwt)
if pending.get('approvals'):
    sdk.approve_approval(pending['approvals'][0]['id'], agent_id='agent-123', jwt_token=owner_jwt)

Webhooks

import os

owner_jwt = os.environ['OWNER_JWT']

webhook = sdk.create_webhook(
    url='https://example.com/webhooks',
    event_types=['payment.*', 'agreement.*'],
    agent_id='agent-123',
    jwt_token=owner_jwt,
)

print(webhook.get('id'))

Verify Webhook Signatures

from paegents import verify_webhook_signature

verify_webhook_signature(signature_header, raw_body, os.environ['PAEGENTS_WEBHOOK_SECRET'])

Error Handling

from paegents import ApiError, PolicyDeniedError

try:
    sdk.ap2_pay(
        intent_mandate_id='intent_123',
        cart_mandate_id='cart_123',
        payment_method=build_card_payment_method(),
    )
except PolicyDeniedError as exc:
    print('Policy denied:', exc)
except ApiError as exc:
    print('API error:', exc)
except Exception as exc:
    print('Unexpected error:', exc)

Notes

  • Prefer SDK methods over manual HTTP calls.
  • Keep API keys and JWTs in environment variables.
  • Use idempotency keys on critical write operations.
  • Bilateral escrow settlement auto-pays on finalization. Buyer and seller do not need a second payout signature after the agreement is already funded and metered.
  • The platform fee is upfront-only in the current live model. Settlement should not perform a second per-call fee pull.

Support

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

paegents-2.9.1.tar.gz (45.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

paegents-2.9.1-py3-none-any.whl (34.7 kB view details)

Uploaded Python 3

File details

Details for the file paegents-2.9.1.tar.gz.

File metadata

  • Download URL: paegents-2.9.1.tar.gz
  • Upload date:
  • Size: 45.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for paegents-2.9.1.tar.gz
Algorithm Hash digest
SHA256 9ac7303e19998eea87ef832747abaa998669f0d4390399be39a6eb660af0765f
MD5 78172905224d1442787890319b8acac6
BLAKE2b-256 8d234a236a105c7cb0c003a5aab92b9e0b2eadd25efe62c8e2e2fafb85df1131

See more details on using hashes here.

File details

Details for the file paegents-2.9.1-py3-none-any.whl.

File metadata

  • Download URL: paegents-2.9.1-py3-none-any.whl
  • Upload date:
  • Size: 34.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for paegents-2.9.1-py3-none-any.whl
Algorithm Hash digest
SHA256 da91d92e40b3c8f375abb441e83860b45b6df03d2245bf9ad3faaa79a576c9af
MD5 4d083a752b526e0c6abd673fce7d420b
BLAKE2b-256 5275cb47d0a676c8ec8e682b6ab9cadce6eaf1b47d12599da66b16841c06422e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page