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. Authentication

Authentication

The SDK supports three authentication paths. Pick the one that matches where your code runs:

You are building… Use Auth mechanism
A bot, script, or backend service PolyesterClient Ed25519 API key
A web app users sign into with a wallet PolyesterBrowserClient Account signer + JWT session
A server rendering pages for signed-in users PolyesterServerClient Session cookies forwarded from the browser

For the underlying model — smart accounts, nonces, signing contracts, MFA — see Authentication model.

API keys (bots and services)

An API key is an Ed25519 keypair. The secret key never leaves your process: the SDK signs each request locally and sends the signature in headers (X-API-KEY-ID, X-API-TIMESTAMP, X-API-SIGNATURE).

Create a key

Generate the keypair client-side and register only the public key. You need an already authenticated client (for example a browser session, or an existing key) to create new keys:

const { publicKey, secretKey } = await client.apiKeys.generateKeypair();

const apiKey = await client.apiKeys.create({
	label: "trading-bot",
	publicKeyEd25519: publicKey.bytes,
	ipWhitelist: ["203.0.113.7"], // optional
});

// Persist secretKey.hex somewhere safe — the SDK never sends it anywhere.
console.log(apiKey?.keyId, secretKey.hex);
MFA step-up
Depending on your account's security settings, apiKeys.create may throw StepUpRequiredError. Complete an MFA challenge and retry with options.stepUpToken — see Error handling.

Use the key

import { PolyesterClient, POLYESTER_TESTNET_ENVIRONMENT, evmHexToBytes } from "@polyester/sdk";

const client = new PolyesterClient({
	environment: POLYESTER_TESTNET_ENVIRONMENT,
	auth: {
		kind: "api-key-ed25519",
		getKeyId: () => process.env.POLYESTER_API_KEY_ID ?? null,
		getSecretKey: () => evmHexToBytes(process.env.POLYESTER_API_SECRET_HEX ?? "0x"),
	},
});

const me = await client.auth.me();
console.log("authenticated as", me.username);

Both getters may return promises, so keys can be fetched from a secrets manager on first use. Keys can be scoped and constrained with policies (client.apiKeys.policies) — market allowlists, action lists, and notional limits.

Wallet login (browsers)

PolyesterBrowserClient manages the full wallet login flow: request a nonce, sign it with an account signer, exchange the signature for a JWT session, and persist it.

1. Create an account signer

An account signer represents the user's Polyester smart account. If you hold a viem LocalAccount (from a browser wallet, Turnkey, or a private key), derive one — no RPC calls needed:

import { createPolyesterAccountSigner } from "@polyester/sdk/account-signer";
import { POLYESTER_TESTNET_ENVIRONMENT } from "@polyester/sdk";

const accountSigner = createPolyesterAccountSigner({
	environment: POLYESTER_TESTNET_ENVIRONMENT,
	owner: ownerAccount, // a viem LocalAccount
});

The signer computes the Safe smart-account address deterministically and produces ERC-6492 wrapped signatures, so login works even before the account is deployed on-chain.

2. Create the client and log in

import { PolyesterBrowserClient, POLYESTER_TESTNET_ENVIRONMENT, createCookieAuthTokenStorage } from "@polyester/sdk";

const client = new PolyesterBrowserClient({
	environment: POLYESTER_TESTNET_ENVIRONMENT,
	accountSigner, // or a factory: () => Promise<AccountSigner | null>
	tokenStorage: createCookieAuthTokenStorage(), // persist across reloads; default is in-memory
});

const result = await client.auth.login({ provider: "metamask" });
console.log("logged in as", result.username, "until", result.expiresAt);

accountSigner also accepts a lazy factory, useful when the wallet connects after the client is constructed; you can also swap it later with client.setAccountSigner(signer).

3. Sessions across reloads

// On app start: restore a persisted session if the token is still valid.
const restored = await client.auth.restoreSession();

// Keep an eye on expiry and refresh with a fresh signature when needed.
if (client.auth.getSessionTimeToExpiry() < 5 * 60_000) {
	await client.auth.refreshSession();
}

// End the session and disconnect private realtime channels.
await client.auth.logout();

The browser auth service also emits events (authenticated, loggedOut, stateChange, …) via client.auth.events, and exposes the current state synchronously with client.auth.getState() — useful for driving UI.

Sessions are environment-bound: a token minted against one environment is ignored when the client is constructed for another (the environment's fingerprint must match).

Switching accounts and subaccounts

client.auth.switchAccount(subaccountId);

After a switch, service calls that omit the account input are scoped to the active subaccount automatically. See Accounts & balances for the full scoping rules.

Server-side sessions

On a server handling a signed-in user's request, build the client from the request cookies — the browser session's bearer token rides along:

import { createPolyesterServerClientFromRequest, POLYESTER_TESTNET_ENVIRONMENT } from "@polyester/sdk";

const client = createPolyesterServerClientFromRequest({
	environment: POLYESTER_TESTNET_ENVIRONMENT,
	request, // the incoming Request
});

const me = await client.verifySession(); // null when not authenticated

See Server-side usage for display sessions, verification, and SSR hydration.

Supplying your own JWT

If you already manage tokens (a custom auth proxy, tests), pass a JWT provider directly to the core client:

const client = new PolyesterClient({
	environment: POLYESTER_TESTNET_ENVIRONMENT,
	auth: { kind: "jwt", getToken: () => myTokenStore.current },
});

Realtime private channels reuse whichever auth provider the client was configured with; override per-connection behavior with the realtime config option if you need different headers for WebSocket auth.

Previous

Architecture

Next

Client configuration

  • API keys (bots and services)
  • Create a key
  • Use the key
  • Wallet login (browsers)
  • 1. Create an account signer
  • 2. Create the client and log in
  • 3. Sessions across reloads
  • Switching accounts and subaccounts
  • Server-side sessions
  • Supplying your own JWT