The server SDK (@blend-money/node) manages accounts and sessions from your backend. It can’t execute transactions directly.
Quick-start flow
Install @blend-money/node
Create a BlendServerSdk instance with your API key
Look up or create an account with sdk.lookupAccount(address)
Scope operations with sdk.forAccount(accountId)
Create a session, quote, and manage the lifecycle manually
Install
pnpm add @blend-money/fe viem
npm install @blend-money/fe viem
yarn add @blend-money/fe viem
pnpm add @blend-money/node
npm install @blend-money/node
yarn add @blend-money/node
Configuration
import { BlendServerSdk } from "@blend-money/node" ;
const sdk = new BlendServerSdk ({
apiKey: process.env. BLEND_API_KEY ! ,
accountTypeId: "savings-usd" ,
});
Field Type Required Description apiKeystringYes Server secret (sk_live_). Never expose in client code. accountTypeIdstringYes The account type (product) to operate on baseUrlstringNo Blend API base URL. Defaults to production. fiatCurrencystringNo ISO 4217 code (e.g. "EUR"). Adds this currency to monetary responses. timeoutMsnumberNo Request timeout in milliseconds. Default: 15000. retriesnumberNo Max retries for 429 and 5xx. Default: 3.
Account management
lookupAccount
Resolves an account by wallet address. Creates the account if it doesn’t exist yet. Does not request Safe deployment on any chain.
const account = await sdk. lookupAccount ( "0x1234...abcd" );
// account.accountId -> "acc_..."
// account.safeAddress -> "0x5678..."
// account.chainsDeployed -> [8453]
Returns a SafeAccountResponse:
Field Type Description accountIdstringBlend account ID safeAddressstringDeterministic Safe address chainsDeployednumber[]Chains where the Safe is live
chainsDeployed only lists chains where the Safe has been deployed. A new account starts with no chains. To deploy on a chain, call safe.request() after lookup. See Safe deployment .
Deploy a Safe
lookupAccount creates the account but does not deploy a Safe. To deploy on a chain, scope the account with forAccount and call safe.request().
const client = sdk. forAccount (account.accountId);
await client.account.safe. request ( 8453 );
Safe deployment is asynchronous. The chain may not appear in chainsDeployed right away. Use safe.resolve() to confirm before your first transaction on that chain.
const resolved = await client.account.safe. resolve ( 8453 );
// resolved.status -> "validated" | "not-deployed" | "invalid" | "disconnected"
If you need the Safe on more chains, request each one:
await client.account.safe. request ( 137 ); // Deploy on Polygon
await client.account.safe. request ( 42161 ); // Deploy on Arbitrum
See Safe deployment for the full resolve-then-request pattern.
forAccount
Returns a scoped BlendClient for a specific account. The client has discover, account, and sessions modules.
const client = sdk. forAccount ( "acc_abc123" );
All session and account operations go through this scoped client.
Session lifecycle
The server SDK cannot execute transactions directly. You must extract the action plan from the session, execute on-chain with your own signer, and then submit the transaction hashes back.
createSession
Creates or retrieves an active session for the scoped account.
const client = sdk. forAccount ( "acc_abc123" );
const session = await client.sessions. createSession ({
externalRef: "order-456" ,
});
quoteDeposit
Quotes a deposit on an open session. Uses inputAssetAddress (not tokenAddress).
const session = await client.sessions. quoteDeposit (intentId, {
chainId: 8453 ,
inputAssetAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" ,
eoa: "0x1234...abcd" ,
amount: "100000000" ,
});
Parameter Type Required Description chainIdnumberYes Origin chain ID inputAssetAddressstringYes Token contract on the origin chain eoastringYes The wallet address holding the tokens amountstringYes Smallest unit, non-negative integer string
quoteWithdraw
Quotes a withdrawal on an open session.
const session = await client.sessions. quoteWithdraw (intentId, {
destinationChainId: 8453 ,
amount: "50000000" ,
isMaxWithdraw: false ,
});
lock
Locks the session with a signer address. Transitions from OPEN to LOCKED.
const session = await client.sessions. lock (intentId, {
signerAddress: "0x1234...abcd" ,
});
submit
Submits transaction hashes after on-chain execution. Transitions from LOCKED to SUBMITTED.
const session = await client.sessions. submit (intentId, {
txHashes: [{ hash: "0xabc..." , chainId: 8453 }],
});
cancel
Cancels a session from any non-terminal state.
const session = await client.sessions. cancel (intentId);
get / list
Retrieve a session by ID or list sessions.
const session = await client.sessions. get (intentId);
const active = await client.sessions. list ({ status: "OPEN" });
execute
High-level orchestrator that handles lock, on-chain execution, submit, and polling. You provide the submitActionPlan callback.
const result = await client.sessions. execute (intentId, {
signerAddress: "0x1234...abcd" ,
submitActionPlan : async ( plan ) => {
// Execute on-chain with your backend signer
const hash = await executeOnChain (plan);
return [{ hash, chainId: plan.chainId }];
},
onStatusChange : ( status ) => console. log (status),
pollIntervalMs: 3000 ,
pollTimeoutMs: 300000 ,
});
Parameter Type Required Description signerAddressstringYes EOA hex address submitActionPlan(plan: ActionPlan) => Promise<Array<{ hash: string; chainId: number }>>Yes Your on-chain execution callback onStatusChange(status: SessionStatus) => voidNo Called on each status transition pollIntervalMsnumberNo Polling interval. Default: 3000. pollTimeoutMsnumberNo Max polling time. Default: 300000 (5 min).
Action plans
Action plans bridge quotes and on-chain execution. Use these utilities to convert API responses into executable plans.
import {
depositQuoteToActionPlan,
withdrawCalldataToActionPlans,
combineActionPlans,
} from "@blend-money/core" ;
depositQuoteToActionPlan
Converts a raw deposit API response to a single ActionPlan with deployType: "direct".
const plan = depositQuoteToActionPlan (session.payload, eoa);
withdrawCalldataToActionPlans
Converts a raw withdrawal API response to an ActionPlan[] (one per source chain) with deployType: "multisend".
const plans = withdrawCalldataToActionPlans (session.payload);
combineActionPlans
Merges plans on the same chainId. "multisend" takes priority over "direct".
const merged = combineActionPlans (plans);
Discovery
Available on the SDK instance without account scoping.
const chains = await sdk.discover. depositChains ();
const tokens = await sdk.discover. depositTokens ( 8453 );
const destinations = await sdk.discover. withdrawDestinations ();
const yieldData = await sdk.discover. yield ();
See SDK Reference for response shapes.
Account data
Available on the scoped client after forAccount().
const client = sdk. forAccount ( "acc_abc123" );
const balance = await client.account. balance ();
const history = await client.account. balanceHistory ();
const positions = await client.account. positions ();
const returns = await client.account. returns ();
See SDK Reference for response shapes.
What to watch out for
Don’t try to call sdk.execute() on the BlendServerSdk instance. The server SDK has no built-in transaction execution. Use client.sessions.execute() with a submitActionPlan callback, or manage the lock/submit lifecycle manually.
Don’t expose your API key (sk_live_) in client code, logs, or version control. It grants full access to your organization’s accounts.
Don’t skip forAccount(). Account-scoped operations like sessions and balances require a scoped client. Calling them on the root SDK will fail.
Don’t forget that quoteDeposit uses inputAssetAddress on the server, not tokenAddress. The frontend SDK uses tokenAddress - the signatures differ.
Deposits & Withdrawals Full deposit and withdrawal flows with cross-chain routing.
SDK Reference Auth, accounts, balances, error codes, and types.