Metadata-Version: 2.4
Name: paegents
Version: 2.9.1
Summary: Official Python SDK for Paegents - Payment infrastructure for AI agents with Service Catalog and Usage Escrow
Author-email: Paegents Inc <support@paegents.com>
License-Expression: MIT
Project-URL: Homepage, https://paegents.com
Project-URL: Documentation, https://docs.paegents.com
Project-URL: Repository, https://github.com/paegents/python-sdk
Project-URL: Bug Tracker, https://github.com/paegents/python-sdk/issues
Keywords: payments,ai,agents,fintech,automation,stripe,braintree,stablecoin
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Operating System :: OS Independent
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.25.0
Requires-Dist: eth-account>=0.8.0
Requires-Dist: x402<0.3,>=0.2.1
Provides-Extra: dev
Requires-Dist: pytest>=6.0; extra == "dev"
Requires-Dist: pytest-cov>=2.0; extra == "dev"
Requires-Dist: black>=21.0.0; extra == "dev"
Requires-Dist: flake8>=3.8.0; extra == "dev"
Requires-Dist: mypy>=0.800; extra == "dev"
Requires-Dist: isort>=5.0.0; extra == "dev"
Dynamic: license-file

# Paegents Python SDK

Official Python SDK for integrating Paegents agent payments.

## Installation

```bash
pip install paegents
```

## Initialize

```python
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

```python
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.

```python
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

```python
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.

```python
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

```python
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

```python
from paegents import verify_webhook_signature

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

## Error Handling

```python
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

- Docs: https://docs.paegents.com
- API docs: https://docs.paegents.com/api
- Support: support@paegents.com

## License

MIT
