Skip to main content

smart402: Python SDK

Deterministic policy engine for AI agent payments via x402.

No LLM in the decision path. Every approve/deny traces to a rule your team configured (not a model's judgment call). A compromised agent cannot reason or prompt-inject its way past smart402.

smart402 currently supports USDC transactions on Base (eip155:8453). Other tokens and chains are on the roadmap.

Install

pip install smart402

For x402 integration extras:

pip install "smart402[x402]"

Python 3.10+ required.

Verify the service is reachable

curl https://api.smart402.com/health
# {"status":"healthy","version":"0.4.0","database":"connected","redis":"connected"}

Before you start

  1. Sign up at https://www.smart402.com
  2. Create an agent in the dashboard
  3. Configure at least one policy (e.g., daily budget of $10)
  4. Create an evaluate-scoped API key in Settings → API Keys
  5. Follow the Quick Start below.

Quick Start

import asyncio
import os
from smart402 import Smart402Client

async def main():
    client = Smart402Client(
        api_key=os.environ["SMART402_AGENT_KEY"],
        agent_id="my-agent-001",
    )

    result = await client.evaluate_payment(
        amount="100000",        # Raw x402 token units as an integer string. "100000" = $0.10 USDC (6 decimals). Pass the value directly from the x402 PaymentRequirements object.
        token="USDC",
        network="eip155:8453",  # Base mainnet (CAIP-2)
        pay_to="0x9dBA414637c611a16BEa6f0796BFcbcBdc410df8",
    )

    print(result.decision)           # "approve" or "deny"
    print(result.triggered_rules)    # [] or ["counterparty_not_on_allowlist", ...]

asyncio.run(main())

Get an API key →

Synchronous usage: asyncio.run() wraps any async call.

x402 Integration

If your agent uses the x402 Python SDK, register smart402 as a lifecycle hook:

from smart402 import smart402_hook
from x402 import x402Client
from x402.mechanisms.evm.exact import register_exact_evm_client
from x402.mechanisms.evm.signers import EthAccountSigner
from eth_account import Account

account = Account.from_key(os.environ["EVM_PRIVATE_KEY"])
signer = EthAccountSigner(account)

client = x402Client()
register_exact_evm_client(client, signer)

# One line to add smart402 protection
client.on_before_payment_creation(
    smart402_hook(
        api_key=os.environ["SMART402_AGENT_KEY"],
        agent_id="my-agent-001",
        agent_wallet_address=signer.address,
    )
)

# Every payment the x402 client makes is now evaluated first

The hook fires before each payment is signed. If smart402 denies the payment, AbortResult is returned and the payment is not made.

Smart402Guard

Smart402Guard is the class-based interface introduced in v0.2.0. It separates evaluation from signing: smart402 evaluates the payment; your wallet signs it. Pass wallet_address as a plain string — works with eth_account, CDP wallets (AgentKit), Privy, hardware wallets, or any signing mechanism.

from smart402 import Smart402Guard

guard = Smart402Guard(
    api_key=os.environ["SMART402_AGENT_KEY"],
    agent_id="my-agent-001",
    wallet_address="0x...",   # plain string — no signer object required
)

# Register with x402:
client.on_before_payment_creation(guard.as_hook())

CDP wallet example

from cdp import CdpClient
from smart402 import Smart402Guard

async with CdpClient() as cdp:
    wallet = await cdp.evm.get_or_create_account(name="my-agent")

    guard = Smart402Guard(
        api_key=os.environ["SMART402_AGENT_KEY"],
        agent_id="my-agent-001",
        wallet_address=wallet.address,  # CDP wallet address as string
        wallet_provider="coinbase",
    )

    client.on_before_payment_creation(guard.as_hook())

Smart402Guard(api_key, agent_id, ...)

Parameter Default Description
api_key required smart402 API key
agent_id required Agent identifier (from dashboard)
wallet_address None Agent's EVM address as a plain string
smart402_url https://api.smart402.com API base URL
fail_mode "fail_open" Behavior when API is unreachable
wallet_provider None e.g. "coinbase", "local_evm"
agent_framework None e.g. "langchain", "langgraph"

guard.as_hook() — returns the async callback for client.on_before_payment_creation(). Equivalent to calling smart402_hook() directly with the same parameters.

Advanced Usage

For full control over all request fields, use the Pydantic models directly:

from smart402 import Smart402Client
from smart402.models import EvaluateRequest, PaymentRequirementsPayload

client = Smart402Client(
    api_key=os.environ["SMART402_AGENT_KEY"],
    agent_id="my-agent-001",
)

result = await client.evaluate(
    EvaluateRequest(
        agent_id=client.agent_id,  # set in the constructor above
        agent_wallet_address="0x...",
        payment_requirements=PaymentRequirementsPayload(
            amount="0.10",
            token="USDC",
            network="eip155:8453",
            pay_to="0x9dBA414637c611a16BEa6f0796BFcbcBdc410df8",
        ),
    )
)

Configuration

Smart402Client(api_key, agent_id, ...)

Parameter Default Description
api_key required smart402 API key
agent_id required Agent identifier (from dashboard)
base_url https://api.smart402.com API base URL

Amount format: Pass amount as raw x402 token units: an integer string such as "100000" for $0.10 USDC (6 decimals). The SDK converts to decimal automatically before sending to the API. Pass the value directly from the x402 PaymentRequirements object without conversion. Raises ValueError if the value is not a positive integer string.

Lifecycle: Smart402Client reuses a single HTTP connection across calls. Call await client.aclose() when done, or use it as an async context manager:

async with Smart402Client(api_key="...", agent_id="...") as client:
    result = await client.evaluate_payment(...)

smart402_hook(api_key, agent_id, ...)

Parameter Default Description
api_key required smart402 API key
agent_id required Agent identifier (from dashboard)
smart402_url https://api.smart402.com API base URL
fail_mode "fail_open" Behavior when API is unreachable
agent_wallet_address None Agent's public EVM address
wallet_provider None e.g. "coinbase", "local_evm"
agent_framework None e.g. "langchain", "langgraph"

Fail-Open vs Fail-Closed

# fail_open (default): if smart402 is unreachable, payment proceeds
smart402_hook(api_key="...", agent_id="...", fail_mode="fail_open")

# fail_closed: if smart402 is unreachable, payment is blocked
smart402_hook(api_key="...", agent_id="...", fail_mode="fail_closed")
Mode When API is unreachable
fail_open (default) Warning logged, payment proceeds
fail_closed AbortResult returned to x402; payment is not made

Error Handling

result = await client.evaluate_payment(
    amount="100000", token="USDC",
    network="eip155:8453", pay_to="0x...",
)
if result.decision == "deny":
    print("Blocked by:", result.triggered_rules)
    print("Evaluation ID:", result.evaluation_id)

When using smart402_hook(), a denied payment returns AbortResult to the x402 client; the payment is not made and no exception is raised to your code. When calling Smart402Client.evaluate() directly, check result.decision; the client always returns the response, never raises on denial.

What data leaves your machine

The SDK sends to the smart402 API:

  • amount, token, network, recipient address
  • agent ID and wallet address (public, not private key)

The SDK never sends:

  • Private keys, seed phrases, or wallet passwords
  • Signed transactions or raw transaction data
  • Wallet balances

One HTTPS call to POST /evaluate. No telemetry, no analytics, no side-channel requests. Verify: the SDK is ~200 lines of code. Read it.

Read the full trust model: SECURITY.md

Limits

  • Rate limit: 600 requests per minute per account
  • Typical latency: 10–50ms (p50), under 200ms (p99)
  • If the API is unreachable, fail_open (default) lets the payment proceed. fail_closed blocks it.
  • The SDK does not retry on failure: it returns the error immediately, keeping latency predictable and letting you own retry logic.
  • Default request timeout: 5 seconds

API Reference

Full endpoint documentation: API.md

License

Apache 2.0. See LICENSE

Metadata

Release files for smart402 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for smart402 0.5.0
File Size Uploaded Provenance Actions
smart402-0.5.0.tar.gz 17.2 kB Without provenance Details for smart402-0.5.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for smart402 0.5.0
File Interpreter ABI Platform Actions
smart402-0.5.0-py3-none-any.whl Python 3 none any Details for smart402-0.5.0-py3-none-any.whl

Total release size: 29.7 kB

Release files / smart402-0.5.0.tar.gz

Download URL smart402-0.5.0.tar.gz
Size 17.2 kB
Tags Source
SHA-256 checksum
How to use checksums
ea5a20e01d5e4dfcd64bc8c5b5bbe724ac9c4f217b32aff181be26b0cf494312
BLAKE2b-256 checksum
How to use checksums
eaea43af048ddc4aa3847b7acf6dc747ec4b1b0496f7227cb276388f320c14af
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.12

Release files / smart402-0.5.0-py3-none-any.whl

Download URL smart402-0.5.0-py3-none-any.whl
Size 12.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
88c43555d428c903bcdb496165e93080787327e1739788feae4ce604b43d4bae
BLAKE2b-256 checksum
How to use checksums
4b43f1c2d9dfdff2e74fa4dc3ece31122e6d2db22433ea456d2fd6c4929520a6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.12

Security for smart402 0.5.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page