Open-source spend governance for AI agents. Issue single-use virtual cards or pay x402-enabled APIs with USDC — with policy enforcement, audit logging, and human-in-the-loop approval.
Agent (LangGraph / CrewAI / any framework)
│
▼
┌────────────────────────────────────────┐
│ AgentWallet │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Policy Engine│ │ Audit Logger │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ ┌──────▼─────────────────▼─────────┐ │
│ │ Payment Rails │ │
│ │ Card: Mock │ Stripe │ StripeMPP │ │
│ │ │ │ │ │ x402: X402Gateway │ MockX402 │ │
│ └──────────────────────────────────┘ │
└────────────────────────────────────────┘
pip install paygraphWith LangGraph support:
pip install paygraph[langgraph]With CrewAI support:
pip install paygraph[crewai]With x402 support (EVM + Solana USDC payments):
pip install paygraph[x402]With live demo (includes LLM providers):
pip install paygraph[live]frompaygraphimportAgentWallet, SpendPolicy, MockGatewaywallet=AgentWallet(
gateway=MockGateway(auto_approve=True),
policy=SpendPolicy(
max_transaction=25.0,
daily_budget=100.0,
blocked_vendors=["doordash"],
),
)
result=wallet.request_spend(
amount=4.20,
vendor="Anthropic API",
justification="Need Claude credits for document summarization.",
)
print(result) # Card approved. PAN: 4111..., CVV: 123, Expiry: 12/28frompaygraphimportAgentWallet, SpendPolicy, StripeCardGatewaywallet=AgentWallet(
gateway=StripeCardGateway(api_key="sk_test_..."),
policy=SpendPolicy(max_transaction=50.0),
)
result=wallet.request_spend(
amount=4.20,
vendor="Anthropic API",
justification="API credits for task completion.",
)StripeCardGateway accepts the following parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
api_key | str | required | Stripe secret key (sk_test_... or sk_live_...) |
cardholder_id | str | None | None | Existing cardholder ID to use (skips auto-creation) |
currency | str | "usd" | Card currency (e.g. "eur", "gbp") |
billing_address | dict | None | US address | Cardholder billing address (line1, city, postal_code, country) |
single_use | bool | True | Mint a new card per transaction; set False to reuse one card |
When single_use=False, a single card is created on the first spend and reused for subsequent calls (spending limit is updated each time).
For sellers that accept Stripe machine payments (MPP / agentic commerce), use StripeMPPGateway to issue scoped Shared Payment Tokens instead of virtual cards.
Requires Stripe machine payments access (preview API). See Stripe docs.
frompaygraphimportAgentWallet, SpendPolicy, StripeMPPGatewaywallet=AgentWallet(
gateway=StripeMPPGateway(
api_key="sk_test_...",
payment_method="pm_...",
grantee="profile_...",
),
policy=SpendPolicy(max_transaction=50.0),
)
result=wallet.request_spend(
amount=4.20,
vendor="Anthropic API",
justification="API credits for task completion.",
)
print(result) # SPT approved. Token: spt_... (spend limit: $4.20)| Parameter | Type | Default | Description |
|---|---|---|---|
api_key | str | required | Stripe secret key (sk_test_... or sk_live_...) |
payment_method | str | required | Saved PaymentMethod id (pm_...) |
grantee | str | required | Seller identifier (typically profile_...) |
currency | str | "usd" | ISO currency code |
expires_in_seconds | int | 3600 | Token lifetime from issuance in seconds |
x402 is an HTTP 402-based protocol for machine-to-machine payments. Instead of minting a card, the gateway makes an HTTP request, handles the 402→sign→retry cycle on-chain, and returns the API response.
frompaygraphimportAgentWallet, X402Gateway, SpendPolicywallet=AgentWallet(
x402_gateway=X402Gateway(evm_private_key="0x..."), # Base/Polygon USDCpolicy=SpendPolicy(max_transaction=5.0),
)
response=wallet.request_x402(
url="https://api.example.com/paid-endpoint",
amount=0.01,
vendor="ExampleAPI",
justification="Need data for analysis.",
)
print(response) # The API response bodySupports both EVM (Base, Polygon, etc.) and Solana:
# Solana onlygateway=X402Gateway(svm_private_key="BASE58_KEY")
# Both networksgateway=X402Gateway(evm_private_key="0x...", svm_private_key="BASE58_KEY")For testing without blockchain:
frompaygraphimportAgentWallet, MockX402Gatewaywallet=AgentWallet(
x402_gateway=MockX402Gateway(auto_approve=True, response_body='{"data": 42}'),
)frompaygraphimportAgentWallet, SpendPolicy, MockX402Gatewaywallet=AgentWallet(
x402_gateway=MockX402Gateway(auto_approve=True),
policy=SpendPolicy(max_transaction=25.0, blocked_vendors=["doordash"]),
)
# wallet.spend_tool → card payments, wallet.x402_tool → x402 API paymentstools= [wallet.spend_tool, wallet.x402_tool]
# Use with LangGraphfromlanggraph.prebuiltimportcreate_react_agentfromlangchain_anthropicimportChatAnthropicllm=ChatAnthropic(model="claude-sonnet-4-6")
agent=create_react_agent(llm, tools=tools)
result=agent.invoke({"messages": [("user", "Buy $4.20 in API credits from Anthropic")]})pip install paygraph[crewai]fromcrewaiimportAgent, Task, CrewfrompaygraphimportAgentWallet, SpendPolicy, MockGatewaywallet=AgentWallet(
gateway=MockGateway(auto_approve=True),
policy=SpendPolicy(max_transaction=25.0),
)
agent=Agent(
role="Purchasing Agent",
goal="Buy API credits when needed",
tools=[wallet.crewai_tool],
)pip install paygraph[mcp]Run the MCP server over stdio:
PAYGRAPH_GATEWAY=mock \
PAYGRAPH_DAILY_BUDGET=100 \
PAYGRAPH_MAX_TRANSACTION=25 \
paygraph-mcpClaude Desktop config:
{
"mcpServers": {
"paygraph": {
"command": "paygraph-mcp",
"env": {
"PAYGRAPH_GATEWAY": "mock",
"PAYGRAPH_DAILY_BUDGET": "100",
"PAYGRAPH_MAX_TRANSACTION": "25"
}
}
}
}SpendPolicy accepts the following parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
max_transaction | float | 50.0 | Maximum amount per transaction (dollars) |
daily_budget | float | 200.0 | Maximum total spend per day |
allowed_vendors | list[str] | None | None | If set, only these vendors are allowed (case-insensitive substring match) |
blocked_vendors | list[str] | None | None | Vendors that are always denied (case-insensitive substring match) |
allowed_mccs | list[int] | None | None | Merchant category code allowlist |
require_justification | bool | True | Require non-empty justification string |
| Variable | Required for | Description |
|---|---|---|
ANTHROPIC_API_KEY | --live (default) | Anthropic API key for Claude LLM |
OPENAI_API_KEY | --live --model openai | OpenAI API key for GPT LLM |
STRIPE_API_KEY | --stripe or --stripe-mpp | Stripe secret key (sk_test_ or sk_live_) |
STRIPE_CURRENCY | Stripe gateways (optional) | Currency for card/SPT limits (default: usd) |
STRIPE_BILLING_COUNTRY | --stripe (optional) | Billing address country code (e.g. FR) |
STRIPE_CARDHOLDER_ID | --stripe (optional) | Reuse an existing cardholder ID |
STRIPE_MPP_PAYMENT_METHOD | --stripe-mpp | PaymentMethod id (pm_...) for SPT issuance |
STRIPE_MPP_GRANTEE | --stripe-mpp | Seller grantee id (typically profile_...) |
STRIPE_MPP_EXPIRES_IN_SECONDS | --stripe-mpp (optional) | SPT lifetime in seconds (default: 3600) |
EVM_PRIVATE_KEY | x402 (EVM) | EVM private key for Base/Polygon USDC payments |
SVM_PRIVATE_KEY | x402 (Solana) | Solana private key (base58) for USDC payments |
Copy .env.example to .env and fill in your keys:
cp .env.example .env# Run simulated demo (no API keys needed)
paygraph demo
# Run live demo with a real LLMexport ANTHROPIC_API_KEY=sk-ant-...
paygraph demo --live
# Use OpenAI insteadexport OPENAI_API_KEY=sk-...
paygraph demo --live --model openai
# Use Stripe Issuing for real card issuanceexport STRIPE_API_KEY=sk_test_...
paygraph demo --live --stripe
# Use Stripe MPP (Shared Payment Tokens)export STRIPE_API_KEY=sk_test_...
export STRIPE_MPP_PAYMENT_METHOD=pm_...
export STRIPE_MPP_GRANTEE=profile_...
paygraph demo --live --stripe-mppMIT