TESTNET
Markets
Trade
Lending Vaults
More
User Docs Developer Docs Sdk API Docs Help
Overview
Overview
Architecture
Authentication
Client configuration
Overview
Environments
Installation
Market data
Market data services
Authentication model
Quickstart
Trading
Trading services
Account services
Catalog & precision
Streaming
Accounts & balances
Funding services
Deposits & withdrawals
Realtime client
Errors
Server-side usage
Error handling
  1. Typescript
  2. /
  3. Accounts & balances

Accounts & balances

Account scoping

A Polyester user has one main account and any number of subaccounts. Almost every authenticated read/write accepts an optional account field that says which one the call is about:

await client.balances.list(); // the "active" account (see below)
await client.balances.list({ account: "main" }); // force the main account
await client.balances.list({ account: { subaccountId } }); // a specific subaccount

The default, "active", resolves through the client:

  • PolyesterBrowserClient — the account the user selected via auth.switchAccount(...).
  • PolyesterServerClient — the main account, unless you opt into the display session's active account with useDisplaySessionActiveAccountAsDefault.
  • PolyesterClient — the main account (no resolver).
Authorization stays server-side
The account field expresses intent; the backend independently verifies the authenticated caller may access that account.

Subaccounts

Full lifecycle management via client.subaccounts:

const { subaccounts, totalCreated } = await client.subaccounts.list();
const detail = await client.subaccounts.get({ subaccountId }); // + apiKeys, policy, members, invites

await client.subaccounts.update({ subaccountId, label: "market-making" });
await client.subaccounts.delete({ subaccountId });

Creating a subaccount requires a signed smart-account proof. In the browser the auth service does the signing for you:

const { subaccountId } = await client.auth.createSubaccount({
	accountSigner: subaccountSigner, // derived with a distinct saltNonce
	label: "experiments",
});

Sharing and roles

Subaccounts can be shared with other users by invitation, with per-member roles and an optional MFA requirement:

await client.subaccounts.inviteMember({ subaccountId, username, role });
await client.subaccounts.listInvites({ direction: "outgoing" });
await client.subaccounts.respondInvite({ inviteId, accept: true });
await client.subaccounts.updateMemberRole({ subaccountId, accountId, role });
await client.subaccounts.setMemberMfaRequirement({ subaccountId, accountId, required: true });
await client.subaccounts.removeMember({ subaccountId, accountId });

// audit trail
const { events } = await client.subaccounts.listEvents({ subaccountId });

Policies

Policies are reusable permission templates — market allowlists, allowed actions, exposure and transfer limits — that attach to subaccounts or API keys:

// Subaccount policies
const policies = await client.subaccounts.policies.list();
const policy = await client.subaccounts.policies.create({ /* scopes, actions, limits */ });
await client.subaccounts.policies.apply({ subaccountId, policyId: policy.policyId });

// API key policies
await client.apiKeys.policies.create({ /* ... */ });
await client.apiKeys.policies.apply({ keyId, policyId });

Balances

const balances = await client.balances.list();
for (const b of balances) {
	console.log(b); // per-asset balances as decimal strings
}

History comes in two shapes — per-asset balance history and account equity history — both columnar and chart-ready:

const history = await client.balances.getBalanceHistory({
	startTsSec,
	endTsSec,
});

const equity = await client.balances.getEquityHistory({
	startTsSec,
	endTsSec,
});

Live updates:

const unsubscribe = client.balances.subscribe({
	accountId,
	onEvent: (balance) => updateUI(balance),
});

Resolving other accounts

To send an internal transfer you need a destination. client.accounts.resolve turns whatever the user typed — username, account id, or smart-account address — into concrete destinations:

const matches = await client.accounts.resolve({
	query: "hunter",
	includeSubaccounts: true,
});

Profile

const profile = await client.auth.profile.get();
await client.auth.profile.update({ /* mutable fields */ });
const history = await client.auth.profile.getUsernameHistory();

Address book

Saved transfer destinations with tags, whitelists, and recent counterparties live under client.addressBook — see the Account services reference for the full method list. The composite getView() powers a dashboard in one call, and subscribeViewInvalidations tells you when to refetch it.

MFA

client.mfa manages TOTP and passkey enrollment, challenges, recovery codes, and step-up proofs. The common flow — a mutation throws StepUpRequiredError, you complete a challenge and retry with stepUpToken — is covered in Error handling.

Previous

Streaming

Next

Funding services

  • Account scoping
  • Subaccounts
  • Sharing and roles
  • Policies
  • Balances
  • Resolving other accounts
  • Profile
  • Address book
  • MFA