Repository files navigation

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.

v0.4.0: Confirmed on-chain spend tracking. Budgets now reflect payments that actually landed on-chain.

Install

pip install smart402

For x402 integration extras:

pip install "smart402[x402]"

Python 3.10+ required.

Before you start

  1. Sign up at https://smart402-dashboard.vercel.app
  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

importasyncioimportosfromsmart402importSmart402Clientasyncdefmain():
client=Smart402Client(
api_key=os.environ["SMART402_AGENT_KEY"],
agent_id="my-agent-001",
)
result=awaitclient.evaluate_payment(
amount="0.10", # USDC amount as a decimal string. "0.10" = ten cents. The Python SDK expects pre-converted dollar decimals.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. A sync API is planned for v0.2.

x402 Integration

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

fromsmart402importsmart402_hookfromx402importx402Clientfromx402.mechanisms.evm.exactimportregister_exact_evm_clientfromx402.mechanisms.evm.signersimportEthAccountSignerfrometh_accountimportAccountaccount=Account.from_key(os.environ["EVM_PRIVATE_KEY"])
signer=EthAccountSigner(account)
client=x402Client()
register_exact_evm_client(client, signer)
# One line to add smart402 protectionclient.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.

Advanced Usage

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

fromsmart402importSmart402Clientfromsmart402.modelsimportEvaluateRequest, PaymentRequirementsPayloadclient=Smart402Client(
api_key=os.environ["SMART402_AGENT_KEY"],
agent_id="my-agent-001",
)
result=awaitclient.evaluate(
EvaluateRequest(
agent_id=client.agent_id, # set in the constructor aboveagent_wallet_address="0x...",
payment_requirements=PaymentRequirementsPayload(
amount="0.10",
token="USDC",
network="eip155:8453",
pay_to="0x9dBA414637c611a16BEa6f0796BFcbcBdc410df8",
),
)
)

Configuration

Smart402Client(api_key, agent_id, ...)

ParameterDefaultDescription
api_keyrequiredsmart402 API key
agent_idrequiredAgent identifier (from dashboard)
base_urlhttps://streetsmart-api.fly.devAPI base URL

Amount format: Pass amount as a decimal dollar string — "0.10" = ten cents. The Python SDK expects pre-converted dollar decimals. If you're reading the amount from an x402 PaymentRequirements object (which uses raw USDC units), convert it first: str(int(raw_amount) / 1_000_000).

smart402_hook(api_key, agent_id, ...)

ParameterDefaultDescription
api_keyrequiredsmart402 API key
agent_idrequiredAgent identifier (from dashboard)
smart402_urlhttps://streetsmart-api.fly.devAPI base URL
fail_mode"fail_open"Behavior when API is unreachable
agent_wallet_addressNoneAgent's public EVM address
wallet_providerNonee.g. "coinbase", "local_evm"
agent_frameworkNonee.g. "langchain", "langgraph"

Fail-Open vs Fail-Closed

# fail_open (default): if smart402 is unreachable, payment proceedssmart402_hook(api_key="...", agent_id="...", fail_mode="fail_open")
# fail_closed: if smart402 is unreachable, payment is blockedsmart402_hook(api_key="...", agent_id="...", fail_mode="fail_closed")
ModeWhen API is unreachable
fail_open (default)Warning logged, payment proceeds
fail_closedAbortResult returned to x402 — payment is not made

Error Handling

result=awaitclient.evaluate_payment(
amount="0.10", token="USDC",
network="eip155:8453", pay_to="0x...",
)
ifresult.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.

Smart402Denied and Smart402Unavailable are not raised in hook mode.

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

About

Deterministic policy engine for AI agent x402 payments [Python SDK]

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

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.

v0.4.0: Confirmed on-chain spend tracking. Budgets now reflect payments that actually landed on-chain.

Install

pip install smart402

For x402 integration extras:

pip install "smart402[x402]"

Python 3.10+ required.

Before you start

  1. Sign up at https://smart402-dashboard.vercel.app
  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

importasyncioimportosfromsmart402importSmart402Clientasyncdefmain():
client=Smart402Client(
api_key=os.environ["SMART402_AGENT_KEY"],
agent_id="my-agent-001",
)
result=awaitclient.evaluate_payment(
amount="0.10", # USDC amount as a decimal string. "0.10" = ten cents. The Python SDK expects pre-converted dollar decimals.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. A sync API is planned for v0.2.

x402 Integration

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

fromsmart402importsmart402_hookfromx402importx402Clientfromx402.mechanisms.evm.exactimportregister_exact_evm_clientfromx402.mechanisms.evm.signersimportEthAccountSignerfrometh_accountimportAccountaccount=Account.from_key(os.environ["EVM_PRIVATE_KEY"])
signer=EthAccountSigner(account)
client=x402Client()
register_exact_evm_client(client, signer)
# One line to add smart402 protectionclient.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.

Advanced Usage

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

fromsmart402importSmart402Clientfromsmart402.modelsimportEvaluateRequest, PaymentRequirementsPayloadclient=Smart402Client(
api_key=os.environ["SMART402_AGENT_KEY"],
agent_id="my-agent-001",
)
result=awaitclient.evaluate(
EvaluateRequest(
agent_id=client.agent_id, # set in the constructor aboveagent_wallet_address="0x...",
payment_requirements=PaymentRequirementsPayload(
amount="0.10",
token="USDC",
network="eip155:8453",
pay_to="0x9dBA414637c611a16BEa6f0796BFcbcBdc410df8",
),
)
)

Configuration

Smart402Client(api_key, agent_id, ...)

ParameterDefaultDescription
api_keyrequiredsmart402 API key
agent_idrequiredAgent identifier (from dashboard)
base_urlhttps://streetsmart-api.fly.devAPI base URL

Amount format: Pass amount as a decimal dollar string — "0.10" = ten cents. The Python SDK expects pre-converted dollar decimals. If you're reading the amount from an x402 PaymentRequirements object (which uses raw USDC units), convert it first: str(int(raw_amount) / 1_000_000).

smart402_hook(api_key, agent_id, ...)

ParameterDefaultDescription
api_keyrequiredsmart402 API key
agent_idrequiredAgent identifier (from dashboard)
smart402_urlhttps://streetsmart-api.fly.devAPI base URL
fail_mode"fail_open"Behavior when API is unreachable
agent_wallet_addressNoneAgent's public EVM address
wallet_providerNonee.g. "coinbase", "local_evm"
agent_frameworkNonee.g. "langchain", "langgraph"

Fail-Open vs Fail-Closed

# fail_open (default): if smart402 is unreachable, payment proceedssmart402_hook(api_key="...", agent_id="...", fail_mode="fail_open")
# fail_closed: if smart402 is unreachable, payment is blockedsmart402_hook(api_key="...", agent_id="...", fail_mode="fail_closed")
ModeWhen API is unreachable
fail_open (default)Warning logged, payment proceeds
fail_closedAbortResult returned to x402 — payment is not made

Error Handling

result=awaitclient.evaluate_payment(
amount="0.10", token="USDC",
network="eip155:8453", pay_to="0x...",
)
ifresult.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.

Smart402Denied and Smart402Unavailable are not raised in hook mode.

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

About

Deterministic policy engine for AI agent x402 payments [Python SDK]

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

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.

v0.4.0: Confirmed on-chain spend tracking. Budgets now reflect payments that actually landed on-chain.

Install

pip install smart402

For x402 integration extras:

pip install "smart402[x402]"

Python 3.10+ required.

Before you start

  1. Sign up at https://smart402-dashboard.vercel.app
  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

importasyncioimportosfromsmart402importSmart402Clientasyncdefmain():
client=Smart402Client(
api_key=os.environ["SMART402_AGENT_KEY"],
agent_id="my-agent-001",
)
result=awaitclient.evaluate_payment(
amount="0.10", # USDC amount as a decimal string. "0.10" = ten cents. The Python SDK expects pre-converted dollar decimals.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. A sync API is planned for v0.2.

x402 Integration

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

fromsmart402importsmart402_hookfromx402importx402Clientfromx402.mechanisms.evm.exactimportregister_exact_evm_clientfromx402.mechanisms.evm.signersimportEthAccountSignerfrometh_accountimportAccountaccount=Account.from_key(os.environ["EVM_PRIVATE_KEY"])
signer=EthAccountSigner(account)
client=x402Client()
register_exact_evm_client(client, signer)
# One line to add smart402 protectionclient.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.

Advanced Usage

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

fromsmart402importSmart402Clientfromsmart402.modelsimportEvaluateRequest, PaymentRequirementsPayloadclient=Smart402Client(
api_key=os.environ["SMART402_AGENT_KEY"],
agent_id="my-agent-001",
)
result=awaitclient.evaluate(
EvaluateRequest(
agent_id=client.agent_id, # set in the constructor aboveagent_wallet_address="0x...",
payment_requirements=PaymentRequirementsPayload(
amount="0.10",
token="USDC",
network="eip155:8453",
pay_to="0x9dBA414637c611a16BEa6f0796BFcbcBdc410df8",
),
)
)

Configuration

Smart402Client(api_key, agent_id, ...)

ParameterDefaultDescription
api_keyrequiredsmart402 API key
agent_idrequiredAgent identifier (from dashboard)
base_urlhttps://streetsmart-api.fly.devAPI base URL

Amount format: Pass amount as a decimal dollar string — "0.10" = ten cents. The Python SDK expects pre-converted dollar decimals. If you're reading the amount from an x402 PaymentRequirements object (which uses raw USDC units), convert it first: str(int(raw_amount) / 1_000_000).

smart402_hook(api_key, agent_id, ...)

ParameterDefaultDescription
api_keyrequiredsmart402 API key
agent_idrequiredAgent identifier (from dashboard)
smart402_urlhttps://streetsmart-api.fly.devAPI base URL
fail_mode"fail_open"Behavior when API is unreachable
agent_wallet_addressNoneAgent's public EVM address
wallet_providerNonee.g. "coinbase", "local_evm"
agent_frameworkNonee.g. "langchain", "langgraph"

Fail-Open vs Fail-Closed

# fail_open (default): if smart402 is unreachable, payment proceedssmart402_hook(api_key="...", agent_id="...", fail_mode="fail_open")
# fail_closed: if smart402 is unreachable, payment is blockedsmart402_hook(api_key="...", agent_id="...", fail_mode="fail_closed")
ModeWhen API is unreachable
fail_open (default)Warning logged, payment proceeds
fail_closedAbortResult returned to x402 — payment is not made

Error Handling

result=awaitclient.evaluate_payment(
amount="0.10", token="USDC",
network="eip155:8453", pay_to="0x...",
)
ifresult.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.

Smart402Denied and Smart402Unavailable are not raised in hook mode.

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

About

Deterministic policy engine for AI agent x402 payments [Python SDK]

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

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.

v0.4.0: Confirmed on-chain spend tracking. Budgets now reflect payments that actually landed on-chain.

Install

pip install smart402

For x402 integration extras:

pip install "smart402[x402]"

Python 3.10+ required.

Before you start

  1. Sign up at https://smart402-dashboard.vercel.app
  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

importasyncioimportosfromsmart402importSmart402Clientasyncdefmain():
client=Smart402Client(
api_key=os.environ["SMART402_AGENT_KEY"],
agent_id="my-agent-001",
)
result=awaitclient.evaluate_payment(
amount="0.10", # USDC amount as a decimal string. "0.10" = ten cents. The Python SDK expects pre-converted dollar decimals.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. A sync API is planned for v0.2.

x402 Integration

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

fromsmart402importsmart402_hookfromx402importx402Clientfromx402.mechanisms.evm.exactimportregister_exact_evm_clientfromx402.mechanisms.evm.signersimportEthAccountSignerfrometh_accountimportAccountaccount=Account.from_key(os.environ["EVM_PRIVATE_KEY"])
signer=EthAccountSigner(account)
client=x402Client()
register_exact_evm_client(client, signer)
# One line to add smart402 protectionclient.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.

Advanced Usage

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

fromsmart402importSmart402Clientfromsmart402.modelsimportEvaluateRequest, PaymentRequirementsPayloadclient=Smart402Client(
api_key=os.environ["SMART402_AGENT_KEY"],
agent_id="my-agent-001",
)
result=awaitclient.evaluate(
EvaluateRequest(
agent_id=client.agent_id, # set in the constructor aboveagent_wallet_address="0x...",
payment_requirements=PaymentRequirementsPayload(
amount="0.10",
token="USDC",
network="eip155:8453",
pay_to="0x9dBA414637c611a16BEa6f0796BFcbcBdc410df8",
),
)
)

Configuration

Smart402Client(api_key, agent_id, ...)

ParameterDefaultDescription
api_keyrequiredsmart402 API key
agent_idrequiredAgent identifier (from dashboard)
base_urlhttps://streetsmart-api.fly.devAPI base URL

Amount format: Pass amount as a decimal dollar string — "0.10" = ten cents. The Python SDK expects pre-converted dollar decimals. If you're reading the amount from an x402 PaymentRequirements object (which uses raw USDC units), convert it first: str(int(raw_amount) / 1_000_000).

smart402_hook(api_key, agent_id, ...)

ParameterDefaultDescription
api_keyrequiredsmart402 API key
agent_idrequiredAgent identifier (from dashboard)
smart402_urlhttps://streetsmart-api.fly.devAPI base URL
fail_mode"fail_open"Behavior when API is unreachable
agent_wallet_addressNoneAgent's public EVM address
wallet_providerNonee.g. "coinbase", "local_evm"
agent_frameworkNonee.g. "langchain", "langgraph"

Fail-Open vs Fail-Closed

# fail_open (default): if smart402 is unreachable, payment proceedssmart402_hook(api_key="...", agent_id="...", fail_mode="fail_open")
# fail_closed: if smart402 is unreachable, payment is blockedsmart402_hook(api_key="...", agent_id="...", fail_mode="fail_closed")
ModeWhen API is unreachable
fail_open (default)Warning logged, payment proceeds
fail_closedAbortResult returned to x402 — payment is not made

Error Handling

result=awaitclient.evaluate_payment(
amount="0.10", token="USDC",
network="eip155:8453", pay_to="0x...",
)
ifresult.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.

Smart402Denied and Smart402Unavailable are not raised in hook mode.

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

About

Deterministic policy engine for AI agent x402 payments [Python SDK]

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

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.

v0.4.0: Confirmed on-chain spend tracking. Budgets now reflect payments that actually landed on-chain.

Install

pip install smart402

For x402 integration extras:

pip install "smart402[x402]"

Python 3.10+ required.

Before you start

  1. Sign up at https://smart402-dashboard.vercel.app
  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

importasyncioimportosfromsmart402importSmart402Clientasyncdefmain():
client=Smart402Client(
api_key=os.environ["SMART402_AGENT_KEY"],
agent_id="my-agent-001",
)
result=awaitclient.evaluate_payment(
amount="0.10", # USDC amount as a decimal string. "0.10" = ten cents. The Python SDK expects pre-converted dollar decimals.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. A sync API is planned for v0.2.

x402 Integration

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

fromsmart402importsmart402_hookfromx402importx402Clientfromx402.mechanisms.evm.exactimportregister_exact_evm_clientfromx402.mechanisms.evm.signersimportEthAccountSignerfrometh_accountimportAccountaccount=Account.from_key(os.environ["EVM_PRIVATE_KEY"])
signer=EthAccountSigner(account)
client=x402Client()
register_exact_evm_client(client, signer)
# One line to add smart402 protectionclient.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.

Advanced Usage

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

fromsmart402importSmart402Clientfromsmart402.modelsimportEvaluateRequest, PaymentRequirementsPayloadclient=Smart402Client(
api_key=os.environ["SMART402_AGENT_KEY"],
agent_id="my-agent-001",
)
result=awaitclient.evaluate(
EvaluateRequest(
agent_id=client.agent_id, # set in the constructor aboveagent_wallet_address="0x...",
payment_requirements=PaymentRequirementsPayload(
amount="0.10",
token="USDC",
network="eip155:8453",
pay_to="0x9dBA414637c611a16BEa6f0796BFcbcBdc410df8",
),
)
)

Configuration

Smart402Client(api_key, agent_id, ...)

ParameterDefaultDescription
api_keyrequiredsmart402 API key
agent_idrequiredAgent identifier (from dashboard)
base_urlhttps://streetsmart-api.fly.devAPI base URL

Amount format: Pass amount as a decimal dollar string — "0.10" = ten cents. The Python SDK expects pre-converted dollar decimals. If you're reading the amount from an x402 PaymentRequirements object (which uses raw USDC units), convert it first: str(int(raw_amount) / 1_000_000).

smart402_hook(api_key, agent_id, ...)

ParameterDefaultDescription
api_keyrequiredsmart402 API key
agent_idrequiredAgent identifier (from dashboard)
smart402_urlhttps://streetsmart-api.fly.devAPI base URL
fail_mode"fail_open"Behavior when API is unreachable
agent_wallet_addressNoneAgent's public EVM address
wallet_providerNonee.g. "coinbase", "local_evm"
agent_frameworkNonee.g. "langchain", "langgraph"

Fail-Open vs Fail-Closed

# fail_open (default): if smart402 is unreachable, payment proceedssmart402_hook(api_key="...", agent_id="...", fail_mode="fail_open")
# fail_closed: if smart402 is unreachable, payment is blockedsmart402_hook(api_key="...", agent_id="...", fail_mode="fail_closed")
ModeWhen API is unreachable
fail_open (default)Warning logged, payment proceeds
fail_closedAbortResult returned to x402 — payment is not made

Error Handling

result=awaitclient.evaluate_payment(
amount="0.10", token="USDC",
network="eip155:8453", pay_to="0x...",
)
ifresult.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.

Smart402Denied and Smart402Unavailable are not raised in hook mode.

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

About

Deterministic policy engine for AI agent x402 payments [Python SDK]

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

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.

v0.4.0: Confirmed on-chain spend tracking. Budgets now reflect payments that actually landed on-chain.

Install

pip install smart402

For x402 integration extras:

pip install "smart402[x402]"

Python 3.10+ required.

Before you start

  1. Sign up at https://smart402-dashboard.vercel.app
  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

importasyncioimportosfromsmart402importSmart402Clientasyncdefmain():
client=Smart402Client(
api_key=os.environ["SMART402_AGENT_KEY"],
agent_id="my-agent-001",
)
result=awaitclient.evaluate_payment(
amount="0.10", # USDC amount as a decimal string. "0.10" = ten cents. The Python SDK expects pre-converted dollar decimals.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. A sync API is planned for v0.2.

x402 Integration

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

fromsmart402importsmart402_hookfromx402importx402Clientfromx402.mechanisms.evm.exactimportregister_exact_evm_clientfromx402.mechanisms.evm.signersimportEthAccountSignerfrometh_accountimportAccountaccount=Account.from_key(os.environ["EVM_PRIVATE_KEY"])
signer=EthAccountSigner(account)
client=x402Client()
register_exact_evm_client(client, signer)
# One line to add smart402 protectionclient.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.

Advanced Usage

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

fromsmart402importSmart402Clientfromsmart402.modelsimportEvaluateRequest, PaymentRequirementsPayloadclient=Smart402Client(
api_key=os.environ["SMART402_AGENT_KEY"],
agent_id="my-agent-001",
)
result=awaitclient.evaluate(
EvaluateRequest(
agent_id=client.agent_id, # set in the constructor aboveagent_wallet_address="0x...",
payment_requirements=PaymentRequirementsPayload(
amount="0.10",
token="USDC",
network="eip155:8453",
pay_to="0x9dBA414637c611a16BEa6f0796BFcbcBdc410df8",
),
)
)

Configuration

Smart402Client(api_key, agent_id, ...)

ParameterDefaultDescription
api_keyrequiredsmart402 API key
agent_idrequiredAgent identifier (from dashboard)
base_urlhttps://streetsmart-api.fly.devAPI base URL

Amount format: Pass amount as a decimal dollar string — "0.10" = ten cents. The Python SDK expects pre-converted dollar decimals. If you're reading the amount from an x402 PaymentRequirements object (which uses raw USDC units), convert it first: str(int(raw_amount) / 1_000_000).

smart402_hook(api_key, agent_id, ...)

ParameterDefaultDescription
api_keyrequiredsmart402 API key
agent_idrequiredAgent identifier (from dashboard)
smart402_urlhttps://streetsmart-api.fly.devAPI base URL
fail_mode"fail_open"Behavior when API is unreachable
agent_wallet_addressNoneAgent's public EVM address
wallet_providerNonee.g. "coinbase", "local_evm"
agent_frameworkNonee.g. "langchain", "langgraph"

Fail-Open vs Fail-Closed

# fail_open (default): if smart402 is unreachable, payment proceedssmart402_hook(api_key="...", agent_id="...", fail_mode="fail_open")
# fail_closed: if smart402 is unreachable, payment is blockedsmart402_hook(api_key="...", agent_id="...", fail_mode="fail_closed")
ModeWhen API is unreachable
fail_open (default)Warning logged, payment proceeds
fail_closedAbortResult returned to x402 — payment is not made

Error Handling

result=awaitclient.evaluate_payment(
amount="0.10", token="USDC",
network="eip155:8453", pay_to="0x...",
)
ifresult.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.

Smart402Denied and Smart402Unavailable are not raised in hook mode.

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

About

Deterministic policy engine for AI agent x402 payments [Python SDK]

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

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.

v0.4.0: Confirmed on-chain spend tracking. Budgets now reflect payments that actually landed on-chain.

Install

pip install smart402

For x402 integration extras:

pip install "smart402[x402]"

Python 3.10+ required.

Before you start

  1. Sign up at https://smart402-dashboard.vercel.app
  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

importasyncioimportosfromsmart402importSmart402Clientasyncdefmain():
client=Smart402Client(
api_key=os.environ["SMART402_AGENT_KEY"],
agent_id="my-agent-001",
)
result=awaitclient.evaluate_payment(
amount="0.10", # USDC amount as a decimal string. "0.10" = ten cents. The Python SDK expects pre-converted dollar decimals.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. A sync API is planned for v0.2.

x402 Integration

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

fromsmart402importsmart402_hookfromx402importx402Clientfromx402.mechanisms.evm.exactimportregister_exact_evm_clientfromx402.mechanisms.evm.signersimportEthAccountSignerfrometh_accountimportAccountaccount=Account.from_key(os.environ["EVM_PRIVATE_KEY"])
signer=EthAccountSigner(account)
client=x402Client()
register_exact_evm_client(client, signer)
# One line to add smart402 protectionclient.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.

Advanced Usage

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

fromsmart402importSmart402Clientfromsmart402.modelsimportEvaluateRequest, PaymentRequirementsPayloadclient=Smart402Client(
api_key=os.environ["SMART402_AGENT_KEY"],
agent_id="my-agent-001",
)
result=awaitclient.evaluate(
EvaluateRequest(
agent_id=client.agent_id, # set in the constructor aboveagent_wallet_address="0x...",
payment_requirements=PaymentRequirementsPayload(
amount="0.10",
token="USDC",
network="eip155:8453",
pay_to="0x9dBA414637c611a16BEa6f0796BFcbcBdc410df8",
),
)
)

Configuration

Smart402Client(api_key, agent_id, ...)

ParameterDefaultDescription
api_keyrequiredsmart402 API key
agent_idrequiredAgent identifier (from dashboard)
base_urlhttps://streetsmart-api.fly.devAPI base URL

Amount format: Pass amount as a decimal dollar string — "0.10" = ten cents. The Python SDK expects pre-converted dollar decimals. If you're reading the amount from an x402 PaymentRequirements object (which uses raw USDC units), convert it first: str(int(raw_amount) / 1_000_000).

smart402_hook(api_key, agent_id, ...)

ParameterDefaultDescription
api_keyrequiredsmart402 API key
agent_idrequiredAgent identifier (from dashboard)
smart402_urlhttps://streetsmart-api.fly.devAPI base URL
fail_mode"fail_open"Behavior when API is unreachable
agent_wallet_addressNoneAgent's public EVM address
wallet_providerNonee.g. "coinbase", "local_evm"
agent_frameworkNonee.g. "langchain", "langgraph"

Fail-Open vs Fail-Closed

# fail_open (default): if smart402 is unreachable, payment proceedssmart402_hook(api_key="...", agent_id="...", fail_mode="fail_open")
# fail_closed: if smart402 is unreachable, payment is blockedsmart402_hook(api_key="...", agent_id="...", fail_mode="fail_closed")
ModeWhen API is unreachable
fail_open (default)Warning logged, payment proceeds
fail_closedAbortResult returned to x402 — payment is not made

Error Handling

result=awaitclient.evaluate_payment(
amount="0.10", token="USDC",
network="eip155:8453", pay_to="0x...",
)
ifresult.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.

Smart402Denied and Smart402Unavailable are not raised in hook mode.

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

About

Deterministic policy engine for AI agent x402 payments [Python SDK]

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

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.

v0.4.0: Confirmed on-chain spend tracking. Budgets now reflect payments that actually landed on-chain.

Install

pip install smart402

For x402 integration extras:

pip install "smart402[x402]"

Python 3.10+ required.

Before you start

  1. Sign up at https://smart402-dashboard.vercel.app
  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

importasyncioimportosfromsmart402importSmart402Clientasyncdefmain():
client=Smart402Client(
api_key=os.environ["SMART402_AGENT_KEY"],
agent_id="my-agent-001",
)
result=awaitclient.evaluate_payment(
amount="0.10", # USDC amount as a decimal string. "0.10" = ten cents. The Python SDK expects pre-converted dollar decimals.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. A sync API is planned for v0.2.

x402 Integration

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

fromsmart402importsmart402_hookfromx402importx402Clientfromx402.mechanisms.evm.exactimportregister_exact_evm_clientfromx402.mechanisms.evm.signersimportEthAccountSignerfrometh_accountimportAccountaccount=Account.from_key(os.environ["EVM_PRIVATE_KEY"])
signer=EthAccountSigner(account)
client=x402Client()
register_exact_evm_client(client, signer)
# One line to add smart402 protectionclient.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.

Advanced Usage

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

fromsmart402importSmart402Clientfromsmart402.modelsimportEvaluateRequest, PaymentRequirementsPayloadclient=Smart402Client(
api_key=os.environ["SMART402_AGENT_KEY"],
agent_id="my-agent-001",
)
result=awaitclient.evaluate(
EvaluateRequest(
agent_id=client.agent_id, # set in the constructor aboveagent_wallet_address="0x...",
payment_requirements=PaymentRequirementsPayload(
amount="0.10",
token="USDC",
network="eip155:8453",
pay_to="0x9dBA414637c611a16BEa6f0796BFcbcBdc410df8",
),
)
)

Configuration

Smart402Client(api_key, agent_id, ...)

ParameterDefaultDescription
api_keyrequiredsmart402 API key
agent_idrequiredAgent identifier (from dashboard)
base_urlhttps://streetsmart-api.fly.devAPI base URL

Amount format: Pass amount as a decimal dollar string — "0.10" = ten cents. The Python SDK expects pre-converted dollar decimals. If you're reading the amount from an x402 PaymentRequirements object (which uses raw USDC units), convert it first: str(int(raw_amount) / 1_000_000).

smart402_hook(api_key, agent_id, ...)

ParameterDefaultDescription
api_keyrequiredsmart402 API key
agent_idrequiredAgent identifier (from dashboard)
smart402_urlhttps://streetsmart-api.fly.devAPI base URL
fail_mode"fail_open"Behavior when API is unreachable
agent_wallet_addressNoneAgent's public EVM address
wallet_providerNonee.g. "coinbase", "local_evm"
agent_frameworkNonee.g. "langchain", "langgraph"

Fail-Open vs Fail-Closed

# fail_open (default): if smart402 is unreachable, payment proceedssmart402_hook(api_key="...", agent_id="...", fail_mode="fail_open")
# fail_closed: if smart402 is unreachable, payment is blockedsmart402_hook(api_key="...", agent_id="...", fail_mode="fail_closed")
ModeWhen API is unreachable
fail_open (default)Warning logged, payment proceeds
fail_closedAbortResult returned to x402 — payment is not made

Error Handling

result=awaitclient.evaluate_payment(
amount="0.10", token="USDC",
network="eip155:8453", pay_to="0x...",
)
ifresult.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.

Smart402Denied and Smart402Unavailable are not raised in hook mode.

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

About

Deterministic policy engine for AI agent x402 payments [Python SDK]

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages