Skip to main content
Blend’s public integration surface is designed for neobanks, wallets, and fintech products that want user-isolated Safe accounts with Blend-managed infrastructure. The current public implementation is centered on:
  • a single SDK package: @blend-money/sdk
  • integration API routes under /extern/:neobankId/:accountTypeId/*
  • account-scoped reads, quotes, and rebalance requests
  • server-built withdrawal payloads for Safe-side exits
pnpm add @blend-money/sdk viem

The operating model

Blend’s integration model has four core entities:
EntityWhat it representsManaged by
Risk ArchitectThe organization defining strategy intent (equivalent to a curator or risk manager in Morpho)Risk Architect admin portal
BasketThe strategy template linked to a productRisk Architect
AccountTypeYour product configuration for end usersIntegrating neobank
AccountA single end-user account under one account typeCreated per user
SafeThe deterministic Safe wallet tied to that user accountBlend + on-chain deployment flow

What your application owns

Your application is responsible for:
  • creating and managing the user experience
  • collecting the user’s EOA address
  • calling Blend with your API key, neobankId, and accountTypeId
  • persisting any product-specific mappings between your internal user IDs and Blend accountIds
  • executing deposit quote payloads and withdrawal calldata payloads in your wallet or relay layer
Blend is responsible for:
  • account creation and account-scoped API reads
  • deterministic Safe addressing
  • Safe deployment requests and validation
  • deposit quote generation
  • withdrawal calldata generation
  • rebalance request generation
In the current public SDK, the normal lifecycle is:
  1. Instantiate one BlendClient per neobank plus account type
  2. Call client.safe.account(userEoa) to get the user’s accountId and Safe address
  3. Use account.accountId for balances, positions, returns, deposit quotes, and rebalance request flows
  4. Use client.withdraw.getCalldata(...) when the user wants to exit funds
const account = await client.safe.account(userEoa);
const balance = await client.balance.get(account.accountId);
const quote = await client.deposit.getQuote({
  chainId: 8453,
  inputAssetAddress: usdcAddress,
  eoa: userEoa,
  accountId: account.accountId,
  amount: "1000000",
});

Safe lifecycle

The Safe-related endpoints are intentionally split:
  • safe.account(address): returns the Blend account record and deterministic Safe address
  • safe.resolve(address, chainId): checks whether the Safe is already deployed on a specific chain
  • safe.request(address, chainId): requests Safe deployment on a specific chain
Use safe.account() as your default first call. Only reach for safe.resolve() or safe.request() when you need chain-specific deployment behavior.

HTTP vs RPC: what requires what

This is the most important architectural distinction in the SDK. Getting this wrong is the most common integration mistake.

Group 1 — Pure backend HTTP calls: no wallet, no RPC, no user signature needed

Every method in this group makes an Axios HTTP request to the Blend backend. They work from a Node.js server, a Next.js API route, or a browser. The user does not need to connect their wallet for any of these.
SDK methodWhat it does
client.safe.account(eoa)Gets or creates the account record. Returns accountId, safeAddress, chainsDeployed.
client.safe.resolve(eoa, chainId)Checks Safe deployment status on a specific chain.
client.safe.request(eoa, chainId)Requests Safe deployment on a new chain.
client.balance.get(accountId)Per-chain balances and total USD.
client.balance.getHistory(accountId, params?)Historical balance snapshots.
client.positions.get(accountId)All deposit, withdraw, and rebalance events.
client.returns.get(accountId)Cumulative return metrics.
client.yield.get()Current yield breakdown for the account type. No accountId needed.
client.deposit.getTokens(chainId?)Token catalog. Heavily cached — safe to call on page load.
client.deposit.getBalances(eoa, chainId)Non-zero ERC-20 balances for the user’s EOA. Blend’s backend fetches this — no RPC connection required on the client.
client.deposit.getQuote(params)Relay-backed deposit route quote. Returns a quote payload for your bridge execution UI.
client.withdraw.getCalldata(params)Pre-built, ABI-encoded withdrawal calldata across chains. Returns calldata — does not execute it.
client.rebalance.getCandidates()Accounts with open rebalance opportunities.
client.rebalance.createRequest()Creates a rebalance request. Returns a requestId.
client.rebalance.getRequest(id)Polls a rebalance request by ID.

Group 2 — On-chain execution: requires Viem transports and a paymaster

The transports and paymasterTransport fields in BlendClientConfig exist solely for on-chain execution. They are not used by any of the read or quote methods above.
ScenarioWhat you need
Submitting calldata from withdraw.getCalldata() via a relay or TransactionHandlerpaymasterTransport (e.g. Pimlico)
Enabling a Safe module via TransactionHandler.submitEnableModuleTransaction()Viem WalletClient, PublicClient, and paymasterTransport
Executing action plans via TransactionHandler.submitActionPlan()Viem WalletClient, PublicClient, and paymasterTransport
transports is keyed by chain ID and points to your RPC provider (e.g. Alchemy). paymasterTransport points to your ERC-4337 bundler (e.g. Pimlico). Both are required in BlendClientConfig even if you only use read methods, because they are part of the config shape — but they are only exercised when executing on-chain transactions.

Summary

Use these namespaces for reads, quotes, and calldata generation — no wallet connection needed:
  • client.safe
  • client.balance
  • client.positions
  • client.returns
  • client.yield
  • client.deposit
  • client.rebalance
  • client.withdraw (returns calldata only)
Use TransactionHandler and SafeMultisendManager when you need to submit UserOperations on-chain. The deposit quote returned by client.deposit.getQuote() is executed via your Relay integration, not the SDK. There is no public client-built deposit() action in the current @blend-money/sdk surface. Deposits use quote APIs and withdrawals use server-built calldata payloads.

Integration risks to design around

  • accountId is required for client.deposit.getQuote() because Blend resolves the destination Safe from account context
  • client.deposit.getTokens() returns a TokenCatalog with { chains, tokens }, not a flat array
  • client.rebalance.getCandidates() returns { candidates: [...] }, not a bare array
  • client.rebalance.createRequest() returns { requestId: string | null, candidateCount }requestId is null when no candidates exist
  • client.safe.request() returns { message }, not a status wrapper inside data
  • client.withdraw.getCalldata() may omit bridge on max withdrawals because the final amount is not known until settlement

Next steps

Last modified on March 10, 2026