StewardClient
The StewardClient class provides typed methods for all Steward API operations.
Constructor
import { StewardClient } from "@stwd/sdk";
const client = new StewardClient({
baseUrl: string; // Steward API URL (e.g., "https://api.steward.fi")
apiKey?: string; // Tenant API key (X-Steward-Key header)
bearerToken?: string; // Agent JWT (Authorization: Bearer header)
tenantId?: string; // Optional tenant ID header
});
When both apiKey and bearerToken are provided, bearerToken takes priority. Use apiKey for tenant operations and bearerToken for agent operations.
Agent Methods
createWallet
Creates a new agent with encrypted EVM and Solana wallets.
const agent = await client.createWallet(
agentId: string,
name: string,
platformId?: string
): Promise<AgentIdentity>;
const agent = await client.createWallet("my-agent", "My Agent");
// Returns: { id, name, tenantId, walletAddresses: { evm, solana }, createdAt }
listAgents
Lists all agents for the authenticated tenant.
const agents = await client.listAgents(): Promise<AgentIdentity[]>;
getAgent
Gets a single agent by ID.
const agent = await client.getAgent(agentId: string): Promise<AgentIdentity>;
createWalletBatch
Creates multiple agents in one request with optional shared policies.
const result = await client.createWalletBatch(
agents: BatchAgentSpec[],
policies?: PolicyRule[]
): Promise<BatchCreateResult>;
const result = await client.createWalletBatch(
[
{ id: "agent-1", name: "Agent One" },
{ id: "agent-2", name: "Agent Two" },
],
[{ id: "limit", type: "spending-limit", enabled: true, config: { maxPerDay: "1000000000000000000" } }]
);
// Returns: { created: AgentIdentity[], errors: { id, error }[] }
Signing Methods
signTransaction
Signs and optionally broadcasts an EVM transaction.
const result = await client.signTransaction(
agentId: string,
tx: SignTransactionInput
): Promise<SignTransactionResult>;
interface SignTransactionInput {
to: string; // Destination address
value: string; // Wei amount
data?: string; // Calldata
chainId?: number; // Chain ID (default: server's active chain)
broadcast?: boolean; // Broadcast after signing (default: true)
}
// Result is one of:
type SignTransactionResult =
| { txHash: string } // Broadcast success
| { signedTx: string } // Sign-only (broadcast: false)
| { status: "pending_approval"; results: PolicyResult[] }; // Queued
const result = await client.signTransaction("my-agent", {
to: "0xDEX_ROUTER",
value: "50000000000000000",
chainId: 8453,
});
if ("txHash" in result) {
console.log("TX hash:", result.txHash);
} else if ("signedTx" in result) {
console.log("Signed TX:", result.signedTx);
} else {
console.log("Needs approval:", result.results);
}
signTypedData
Signs EIP-712 structured data.
const result = await client.signTypedData(
agentId: string,
input: SignTypedDataInput
): Promise<{ signature: string }>;
signSolanaTransaction
Signs a serialized Solana transaction.
const result = await client.signSolanaTransaction(
agentId: string,
input: SignSolanaTransactionInput
): Promise<SignSolanaTransactionResult>;
interface SignSolanaTransactionInput {
transaction: string; // Base64-encoded serialized transaction
chainId?: number; // 101 = mainnet, 102 = devnet
broadcast?: boolean;
}
signBitcoinPsbt
Signs a scoped Bitcoin PSBT for an agent wallet. Steward returns a signed PSBT
and a Steward transactionId; it does not broadcast.
const result = await client.signBitcoinPsbt(
agentId: string,
input: SignBitcoinPsbtInput
): Promise<SignBitcoinPsbtResult>;
interface SignBitcoinPsbtInput {
walletScope: string; // e.g. "bitcoin:testnet:p2wpkh:0:0:0"
psbtBase64: string; // Base64-encoded PSBT
finalize?: boolean; // Return raw tx metadata when fully finalizable
referenceId?: string; // Caller-stable tracking ID for audit/history lookup
}
interface SignBitcoinPsbtResult {
transactionId: string; // Steward transaction record ID
signedPsbtBase64: string;
signedInputs: number;
walletScope: string;
walletAddress: string;
addressType: "p2wpkh" | "p2tr";
network: "mainnet" | "testnet";
finalizedTxHex?: string;
txId?: string;
vsize?: number;
feeSats?: string;
}
const referenceId = "withdrawal-123";
const result = await client.signBitcoinPsbt("my-agent", {
walletScope: "bitcoin:testnet:p2wpkh:0:0:0",
psbtBase64,
finalize: true,
referenceId,
});
console.log(result.transactionId);
if (result.finalizedTxHex) {
// Steward does not broadcast. Before retrying a downstream broadcast, check
// result.txId with your broadcaster or indexer.
}
Steward decodes standard PSBT destination outputs and enforces fee caps,
spend/address policies, and Bitcoin raw-signing-chain policy requirements before
signing. For safe retries, reuse a stable referenceId and, where accepted by
your deployment, the same Idempotency-Key header. If finalize: true returns
finalizedTxHex, avoid blindly retrying broadcast attempts; first check whether
the returned Bitcoin txId is already known to your broadcaster or indexer.
signMessage
Signs an arbitrary message.
const result = await client.signMessage(
agentId: string,
message: string
): Promise<{ signature: string }>;
Encrypted Key Import Methods
Agent-vault import helpers require a tenant owner/admin bearer session with
recent MFA. User-wallet import helpers require the authenticated user’s bearer
session with recent MFA. Both flows use a server X25519 init response and then
submit only an encrypted envelope; do not include a plaintext privateKey
field.
const agentSession = await client.initializeEncryptedAgentKeyImport(
"my-agent",
"evm"
);
await client.submitEncryptedAgentKeyImport("my-agent", {
importSessionId: agentSession.importSessionId,
ephemeralPublicKey,
iv,
ciphertext,
tag,
});
const userSession = await client.initializeEncryptedUserWalletKeyImport(
"evm",
{ walletIndex: 2 }
);
await client.submitEncryptedUserWalletKeyImport({
importSessionId: userSession.importSessionId,
ephemeralPublicKey,
iv,
ciphertext,
tag,
walletIndex: userSession.aad.walletIndex,
});
Init sessions are bound to tenant/app/user/agent/wallet fields returned in
aad. Wrong-agent and wrong-wallet-index submits fail without consuming the
original session; validated submits consume the session atomically, so replay
fails.
Query Methods
getBalance
Gets the native token balance for an agent’s wallet.
const balance = await client.getBalance(
agentId: string,
chainId?: number
): Promise<AgentBalance>;
getAddresses
Gets all wallet addresses across chain families.
const addresses = await client.getAddresses(
agentId: string
): Promise<GetAddressesResult>;
getHistory
Gets the transaction history for an agent.
const history = await client.getHistory(
agentId: string
): Promise<StewardHistoryEntry[]>;
rpcPassthrough
Proxies a read-only RPC call.
const result = await client.rpcPassthrough(
agentId: string,
input: { method: string; params?: unknown[]; chainId: number }
): Promise<RpcResponse>;
Global Wallet Methods
Global wallet helpers require a user bearer token. Pass walletIndex when the
user selected a non-default embedded wallet; the server binds consent, scan,
confirmation, audit, and execution to that same wallet index plus wallet agent
id/address. Omitting walletIndex selects index 0.
await client.approveGlobalWalletConsent({
appId: "tenant/client",
origin: "https://app.example",
scopes: ["eth_accounts", "eth_sendTransaction"],
walletIndex: 2,
});
const scan = await client.scanGlobalWalletTransaction({
appId: "tenant/client",
origin: "https://app.example",
walletIndex: 2,
params: [{ from, to, value: "0x1", chainId: "0x2105" }],
});
const confirmation = await client.confirmGlobalWalletAction({
appId: "tenant/client",
origin: "https://app.example",
method: "eth_sendTransaction",
walletIndex: 2,
params: [{ from, to, value: "0x1", chainId: "0x2105" }],
});
await client.globalWalletRpc({
appId: "tenant/client",
origin: "https://app.example",
method: "eth_sendTransaction",
walletIndex: 2,
confirmationId: confirmation.confirmationId,
params: [{ from, to, value: "0x1", chainId: "0x2105" }],
});
Contract calldata remains blocked for global wallet transaction execution until
selector-aware scanning and policy checks are configured for that deployment.
App Client Methods
Tenant app-client helpers expose the same TenantAppClient shape documented in
the control-plane API. Native app clients can set iOS and Android allowlists
with allowedBundleIds and allowedPackageNames; device-code auth then checks
X-Steward-Native-Bundle-Id, X-Steward-Native-Package-Name, or matching JSON
fields against the enabled client.
await client.createTenantAppClient("tenant-1", {
id: "ios-prod",
name: "iOS Production",
environment: "production",
enabled: true,
allowedBundleIds: ["com.example.app"],
allowedRedirectUrls: ["com.example.app://auth/callback"],
});
const clients = await client.listTenantAppClients("tenant-1");
Tenant User Methods
Removes selected EVM/Solana linked wallet accounts from tenant members after an
owner/admin has reviewed a one-third-party-wallet policy violation report.
Requires a user bearer token scoped to the tenant with recent MFA.
const result = await client.bulkRemediateTenantWalletPolicyViolations(
tenantId: string,
wallets: Array<{ userId: string; accountId: string }>
): Promise<TenantWalletPolicyBulkRemediationResponse>;
The API caps requests at 50 items and returns per-item success/error results so
callers can retry failed selections without repeating successful remediations.
Policy Methods
getPolicies
Gets all policies for an agent.
const policies = await client.getPolicies(
agentId: string
): Promise<PolicyRule[]>;
setPolicies
Replaces all policies for an agent.
await client.setPolicies(
agentId: string,
policies: PolicyRule[]
): Promise<void>;
Additional API Areas
This page covers the core StewardClient methods. The archived root SDK reference also covered webhook, tenant configuration, dashboard, and approval APIs; those surfaces now live in the API reference:
Error Handling
All methods throw StewardApiError on failure:
import { StewardApiError } from "@stwd/sdk";
try {
await client.signTransaction("my-agent", { ... });
} catch (error) {
if (error instanceof StewardApiError) {
console.error(`HTTP ${error.status}: ${error.message}`);
// For signing errors, data may contain policy results
if (error.data?.results) {
console.error("Policy results:", error.data.results);
}
}
}
class StewardApiError<TData = unknown> extends Error {
readonly status: number; // HTTP status code (0 for network errors)
readonly data?: TData; // Response data (may include policy results)
}