Python SDK
The subcent Python package provides an async client for the Subcent API, with built-in LangChain integration for AI agent workflows.
Installation#
pip install subcent
For LangChain integration:
pip install subcent[langchain]
Quick Start#
from subcent import Subcent
async def main():
sc = Subcent(
api_key="sc_test_your_api_key",
network="testnet",
base_url="https://testnet-api.subcent.io",
)
try:
payment = await sc.payments.request({
"agent_id": "agt_550e8400...",
"merchant_id": "mrc_660e8400...",
"amount": 2500,
"currency": "USDC",
"category": "groceries",
})
print(payment["status"])
finally:
await sc.close()
Client Configuration#
sc = Subcent(
api_key="sc_test_your_api_key",
base_url="https://testnet-api.subcent.io", # default: http://localhost:3000
network="testnet", # "devnet", "testnet", or "mainnet"
)
| Parameter | Type | Required | Description |
|---|---|---|---|
| api_key | str | Yes | Your API key (sc_live_, sc_test_, or sc_agent_ prefix) |
| base_url | str | No | API base URL (default: http://localhost:3000) |
| network | str | No | Network identifier (default: "devnet") |
The client uses httpx.AsyncClient internally and sets the Authorization and Content-Type headers automatically. All API methods are async and must be awaited.
Always call await sc.close() when you are done to release the underlying HTTP connection pool. Use a try/finally block or an async context manager.
Payments#
payments.request(params)#
Submit a payment request.
payment = await sc.payments.request({
"agent_id": "agt_550e8400...",
"merchant_id": "mrc_660e8400...",
"amount": 2500,
"currency": "USDC",
"category": "groceries",
"memo": "Weekly grocery order",
})
print(payment["status"]) # "completed" | "pending_approval" | "rejected"
PaymentRequest TypedDict:
| Field | Type | Required | Description |
|---|---|---|---|
| agent_id | str | Yes | The agent making the payment |
| merchant_id | str | Yes | The merchant receiving the payment |
| amount | int | Yes | Amount in minor units |
| currency | str | No | Currency code (default: "USDC") |
| category | str | Yes | Spending category |
| memo | str | No | Optional payment memo |
payments.get(id)#
Retrieve a specific payment by ID.
payment = await sc.payments.get("pay_770e8400...")
print(payment["status"])
print(payment.get("tx_hash"))
payments.list(**params)#
List payments with optional filters.
result = await sc.payments.list(
vault_id="550e8400...",
agent_id="agt_550e8400...",
status="completed",
limit=20,
)
for p in result["payments"]:
print(p["payment_id"], p["amount"])
payments.approve(id)#
Approve a payment pending human approval.
result = await sc.payments.approve("pay_770e8400...")
print(result["status"]) # "completed"
payments.reject(id, reason=None)#
Reject a payment pending human approval.
result = await sc.payments.reject("pay_770e8400...", reason="Amount too high")
print(result["status"]) # "rejected"
Vaults#
vaults.create(params)#
Create a new vault.
vault = await sc.vaults.create({
"owner_wallet": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"chain": "solana",
"label": "Production Vault",
})
print(vault["vault_id"])
print(vault["vault_address"])
vaults.get(id)#
Retrieve vault details.
vault = await sc.vaults.get("550e8400...")
print(vault["balance"])
print(vault["status"])
vaults.freeze(id)#
Freeze a vault.
result = await sc.vaults.freeze("550e8400...")
print(result["status"]) # "frozen"
vaults.unfreeze(id)#
Unfreeze a vault.
result = await sc.vaults.unfreeze("550e8400...")
print(result["status"]) # "active"
Policies#
policies.create(params)#
Create a spending policy.
policy = await sc.policies.create({
"agent_id": "agt_550e8400...",
"vault_id": "550e8400...",
"budget": {
"max_per_transaction": 100,
"max_daily": 500,
"max_monthly": 5000,
"currency": "USDC",
},
"categories": {
"mode": "allowlist",
"list": ["groceries", "household"],
},
"velocity_controls": {
"max_transactions_per_hour": 10,
"max_transactions_per_day": 50,
},
})
print(policy["policy_id"])
print(policy["policy_hash"])
policies.get(id)#
Retrieve a policy.
policy = await sc.policies.get("pol_880e8400...")
print(policy["budget"]["max_per_transaction"])
policies.update(id, params)#
Update a policy.
updated = await sc.policies.update("pol_880e8400...", {
"budget": {
"max_per_transaction": 200,
"max_daily": 1000,
"currency": "USDC",
},
})
print(updated["version"]) # incremented
Escrows#
escrows.create(params)#
Create a conditional escrow.
escrow = await sc.escrows.create({
"merchant_id": "mrc_660e8400...",
"amount": 50000,
"currency": "USDC",
"condition": {
"type": "delivery_confirmation",
"data": {"tracking_number": "1Z999AA10123456784"},
},
"expires_in_hours": 72,
})
print(escrow["escrow_id"])
print(escrow["status"]) # "funded"
escrows.get(id)#
Retrieve escrow details.
escrow = await sc.escrows.get("esc_990e8400...")
print(escrow["status"])
print(escrow["expires_at"])
escrows.release(id)#
Release escrow funds to the merchant.
result = await sc.escrows.release("esc_990e8400...")
print(result["status"]) # "released"
escrows.refund(id)#
Refund escrow funds back to the vault.
result = await sc.escrows.refund("esc_990e8400...")
print(result["status"]) # "refunded"
Catalog#
catalog.search(merchant_id, **params)#
Search a merchant's product catalog.
result = await sc.catalog.search(
"mrc_660e8400...",
q="organic bananas",
category="groceries",
limit=10,
)
for item in result["items"]:
print(item["name"], item["price"])
CatalogSearchParams:
| Field | Type | Required | Description |
|---|---|---|---|
| q | str | No | Search query |
| category | str | No | Filter by category |
| limit | int | No | Number of results |
| cursor | str | No | Pagination cursor |
LangChain Integration#
The Python SDK includes two LangChain tools for AI agents. Install the extra:
pip install subcent[langchain]
SubcentPaymentTool#
A LangChain BaseTool that allows an agent to make payments.
from subcent import Subcent
from subcent.integrations.langchain import SubcentPaymentTool
sc = Subcent(api_key="sc_agent_your_agent_key", base_url="https://testnet-api.subcent.io")
payment_tool = SubcentPaymentTool(client=sc)
# Use in a LangChain agent
from langchain.agents import AgentExecutor, create_openai_functions_agent
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4")
tools = [payment_tool]
# The tool accepts these parameters:
# - agent_id (str): The agent ID making the payment
# - merchant_id (str): The merchant ID to pay
# - amount (int): Amount in minor units
# - currency (str): Payment currency (default: "USDC")
# - category (str): Payment category
# - memo (str, optional): Payment memo
Tool schema:
| Field | Type | Required | Description |
|---|---|---|---|
| agent_id | str | Yes | The agent ID making the payment |
| merchant_id | str | Yes | The merchant ID to pay |
| amount | int | Yes | Amount in minor units (e.g., cents) |
| currency | str | No | Payment currency (default: "USDC") |
| category | str | Yes | Payment category |
| memo | str | No | Optional payment memo |
The tool returns a string like: "Payment pay_abc123 created with status: completed"
SubcentCatalogTool#
A LangChain BaseTool that allows an agent to search merchant catalogs.
from subcent.integrations.langchain import SubcentCatalogTool
catalog_tool = SubcentCatalogTool(client=sc)
# The tool accepts these parameters:
# - merchant_id (str): Merchant ID to search
# - query (str, optional): Search query
Tool schema:
| Field | Type | Required | Description |
|---|---|---|---|
| merchant_id | str | Yes | Merchant ID to search |
| query | str | No | Search query |
The tool returns a string like: "Found 3 items: Organic Bananas, Fresh Milk, Whole Wheat Bread"
Full LangChain Example#
import asyncio
from subcent import Subcent
from subcent.integrations.langchain import SubcentPaymentTool, SubcentCatalogTool
from langchain.agents import AgentExecutor, create_openai_functions_agent
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
async def main():
sc = Subcent(
api_key="sc_agent_your_agent_key",
base_url="https://testnet-api.subcent.io",
)
tools = [
SubcentPaymentTool(client=sc),
SubcentCatalogTool(client=sc),
]
llm = ChatOpenAI(model="gpt-4")
prompt = ChatPromptTemplate.from_messages([
("system", "You are a shopping assistant. Use the tools to search catalogs and make payments."),
MessagesPlaceholder(variable_name="chat_history", optional=True),
("human", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
agent = create_openai_functions_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
result = await executor.ainvoke({
"input": "Find organic bananas from merchant mrc_freshmart and buy 2 bunches"
})
print(result["output"])
await sc.close()
asyncio.run(main())
Error Handling#
The SDK raises ApiError for all HTTP error responses.
from subcent.errors import ApiError, SubcentError
try:
payment = await sc.payments.request({
"agent_id": "agt_invalid",
"merchant_id": "mrc_660e8400...",
"amount": 2500,
"currency": "USDC",
"category": "groceries",
})
except ApiError as e:
print(e.status_code) # 403
print(e.error_code) # "agent_not_authorized"
print(e.message) # "Agent not found"
print(e.details) # additional error details if available
except SubcentError as e:
print(e.message) # base error class
Error Classes#
| Class | Description |
|---|---|
| SubcentError | Base exception class with a message attribute |
| ApiError | Raised on HTTP error responses. Includes status_code, error_code, message, and details |