TypeScript SDK
The @subcent/sdk package provides a typed client for the Subcent API. It handles authentication, request formatting, and error parsing.
Installation#
npm install @subcent/sdk
Configuration#
import Subcent from "@subcent/sdk";
const sc = new Subcent({
apiKey: "sc_test_your_api_key",
network: "testnet", // "mainnet" or "testnet"
baseUrl: undefined, // optional override
});
SubcentConfig#
| Field | Type | Required | Description |
|---|---|---|---|
| apiKey | string | Yes | Your API key (sc_live_, sc_test_, or sc_agent_ prefix) |
| network | string | Yes | "mainnet" or "testnet" |
| baseUrl | string | No | Override the API base URL. Defaults to https://api.subcent.io/v1 for mainnet, https://testnet-api.subcent.io/v1 for testnet |
Payments#
The payments namespace handles payment requests, approvals, and listing.
payments.request(params)#
Submit a payment request from an agent to a merchant.
const payment = await sc.payments.request({
agent_id: "agt_550e8400...",
merchant_id: "mrc_660e8400...",
amount: "25.00",
currency: "USDC", // optional, defaults to "USDC"
description: "Weekly grocery order",
category: "groceries",
metadata: { order_id: "ord_12345" },
});
console.log(payment.payment_id);
console.log(payment.status); // "completed" | "pending_approval" | "rejected"
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
| agent_id | string | Yes | The agent making the payment |
| merchant_id | string | Yes | The merchant receiving the payment |
| amount | string | Yes | Amount in decimal USDC |
| currency | string | No | Defaults to "USDC" |
| description | string | No | Human-readable description |
| category | string | No | Spending category |
| metadata | object | No | Arbitrary key-value data |
payments.get(paymentId)#
Retrieve a specific payment by ID.
const payment = await sc.payments.get("pay_770e8400...");
console.log(payment.status);
console.log(payment.transaction?.tx_hash);
payments.list(params)#
List payments for a vault with optional filters.
const result = await sc.payments.list({
vault_id: "550e8400...",
agent_id: "agt_550e8400...", // optional
status: "completed", // optional
limit: 20, // optional, default 50, max 200
cursor: undefined, // optional
});
for (const payment of result.payments) {
console.log(payment.payment_id, payment.amount);
}
if (result.has_more) {
// Fetch next page
}
payments.approve(paymentId)#
Approve a payment that is pending human approval.
const result = await sc.payments.approve("pay_770e8400...");
console.log(result.status); // "completed"
console.log(result.transaction.tx_hash);
payments.reject(paymentId, reason?)#
Reject a payment that is pending human approval.
const result = await sc.payments.reject("pay_770e8400...", "Amount too high");
console.log(result.status); // "rejected"
console.log(result.reason); // "Amount too high"
Vaults#
The vaults namespace manages on-chain smart-contract wallets.
vaults.create(params)#
Create a new vault on the specified blockchain.
const vault = await sc.vaults.create({
owner_wallet: "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
chain: "solana", // "solana" or "base"
label: "Production Vault",
});
console.log(vault.vault_id);
console.log(vault.vault_address);
console.log(vault.status); // "active"
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
| owner_wallet | string | Yes | The wallet address that owns this vault |
| chain | string | Yes | "solana" or "base" |
| label | string | No | Human-readable label |
vaults.get(vaultId)#
Retrieve vault details including balance and status.
const vault = await sc.vaults.get("550e8400...");
console.log(vault.balance); // "1250.500000"
console.log(vault.status); // "active" | "frozen"
console.log(vault.authorized_agents);
vaults.freeze(vaultId)#
Freeze a vault to block all transactions immediately.
const result = await sc.vaults.freeze("550e8400...");
console.log(result.status); // "frozen"
console.log(result.frozen_at);
vaults.unfreeze(vaultId)#
Unfreeze a previously frozen vault.
const result = await sc.vaults.unfreeze("550e8400...");
console.log(result.status); // "active"
console.log(result.unfrozen_at);
Agents#
The agents namespace handles agent registration and revocation.
agents.create(params)#
Register a new agent authorized to spend from a vault.
const agent = await sc.agents.create({
vault_id: "550e8400...",
label: "Shopping Assistant",
framework: "langchain",
webhook_url: "https://your-app.com/webhooks/agent",
});
console.log(agent.agent_id);
console.log(agent.api_key); // "sc_agent_..." -- shown only once!
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
| vault_id | string | Yes | The vault to authorize |
| label | string | No | Human-readable name |
| framework | string | No | AI framework identifier |
| webhook_url | string | No | URL for agent-specific events |
The api_key is returned only in this response. Store it in a secrets manager immediately.
agents.revoke(agentId)#
Permanently revoke an agent's access.
const result = await sc.agents.revoke("agt_550e8400...");
console.log(result.status); // "revoked"
Policies#
The policies namespace manages spending policies for agents.
policies.create(params)#
Create a new spending policy bound to an agent and vault.
const 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,
},
approval_rules: [
{
condition: "amount > 50",
action: "request_approval",
timeout_seconds: 3600,
fallback: "reject",
},
],
escrow_rules: {
require_escrow_above: 200,
default_escrow_expiry_hours: 48,
},
});
console.log(policy.policy_id);
console.log(policy.policy_hash); // "sha256:..."
console.log(policy.version); // 1
See the Policies API reference for the full schema of all fields.
policies.get(policyId)#
Retrieve a policy with its full configuration.
const policy = await sc.policies.get("pol_880e8400...");
console.log(policy.budget.max_per_transaction);
console.log(policy.version);
policies.update(policyId, params)#
Update an existing policy. Increments the version number.
const updated = await sc.policies.update("pol_880e8400...", {
budget: {
max_per_transaction: 200,
max_daily: 1000,
currency: "USDC",
},
});
console.log(updated.version); // 2
console.log(updated.policy_hash); // new hash
Merchants#
The merchants namespace handles merchant registration and catalog access.
merchants.register(params)#
Register a new merchant.
const merchant = await sc.merchants.register({
wallet_address: "8xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
chain: "solana",
business_name: "FreshMart Groceries",
category: "groceries",
catalog_url: "https://api.freshmart.com/catalog",
webhook_url: "https://api.freshmart.com/webhooks",
});
console.log(merchant.merchant_id);
console.log(merchant.status); // "pending_verification"
merchants.getCatalog(merchantId)#
Retrieve a merchant's product catalog.
const catalog = await sc.merchants.getCatalog("mrc_660e8400...");
console.log(catalog.total_items);
for (const item of catalog.items) {
console.log(item.name, item.price);
}
Escrows#
The escrows namespace handles conditional payment escrows.
escrows.create(params)#
Create a new escrow with conditional release terms.
const escrow = await sc.escrows.create({
agent_id: "agt_550e8400...",
merchant_id: "mrc_660e8400...",
amount: "500.00",
currency: "USDC",
condition: {
type: "delivery_confirmation",
data: { tracking_number: "1Z999AA10123456784" },
},
expires_in_hours: 72,
});
console.log(escrow.escrow_id);
console.log(escrow.status); // "funded"
Error Handling#
All SDK methods throw AppError when the API returns an error response. The error includes the error code, HTTP status, and a human-readable message.
import { AppError } from "@subcent/sdk";
try {
await sc.payments.request({
agent_id: "agt_invalid",
merchant_id: "mrc_660e8400...",
amount: "25.00",
});
} catch (err) {
if (err instanceof AppError) {
console.log(err.errorCode); // "AGENT_NOT_AUTHORIZED"
console.log(err.httpStatus); // 403
console.log(err.message); // "Agent not found"
console.log(err.toJSON());
// { "error": "agent_not_authorized", "message": "Agent not found" }
}
}
Error Code Reference#
The SDK re-exports AppError from @subcent/shared. It maps API error responses back to typed error codes:
| Error Code | HTTP Status | Typical Cause |
|---|---|---|
| VAULT_NOT_FOUND | 404 | Invalid vault ID |
| VAULT_FROZEN | 403 | Vault is frozen |
| INSUFFICIENT_BALANCE | 400 | Not enough USDC in vault |
| AGENT_NOT_AUTHORIZED | 403 | Invalid or unauthorized agent |
| AGENT_REVOKED | 403 | Agent has been revoked |
| POLICY_VIOLATION | 400 | Payment violates spending policy |
| POLICY_NOT_FOUND | 404 | No active policy for agent |
| MERCHANT_NOT_FOUND | 404 | Invalid merchant ID |
| MERCHANT_SUSPENDED | 403 | Merchant is suspended |
| RATE_LIMIT_EXCEEDED | 429 | Too many requests |
| CHAIN_ERROR | 502 | Blockchain communication error |
| INTERNAL_ERROR | 500 | Unexpected server error |