Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.blend.money/llms.txt

Use this file to discover all available pages before exploring further.

Authentication, account management, balance queries, error handling, and TypeScript type definitions.

Authentication

SIWE flow (frontend)

The frontend SDK uses Sign-In with Ethereum. The user’s wallet signs a challenge message. Blend returns a JWT that binds the wallet to a Blend account. The JWT contains the wallet address, account ID, and account type. Every SDK call after sign-in includes it automatically. You never pass an account ID.

API key auth (server)

The server SDK authenticates with an API key (sk_live_) in the X-API-Key header. No challenge/verify step. The API key replaces both the publishable key and the SIWE session.
import { BlendServerSdk } from "@blend-money/node";

const sdk = new BlendServerSdk({
  apiKey: process.env.BLEND_API_KEY!,
  accountTypeId: "savings-usd",
});
You pass accountId explicitly to every account-scoped call via sdk.forAccount(accountId).

Rate limits

EndpointLimitScoped by
SIWE challenge (POST /auth/challenge)10 / 60 secondsIP address
SIWE verify (POST /auth/verify)5 / 60 secondsIP address
Account-scoped routes (frontend)100 / 60 secondsWallet address
Account-scoped routes (server)100 / 60 secondsAPI key
Exceeding limits returns a 429 with error code RATE_LIMITED or RATE_LIMIT.

Session security

  • Keep JWTs in memory. Use sessionStorage for persistence across reloads. Avoid localStorage.
  • Clear the session when the wallet disconnects or address changes.
  • Never share an exported session across users or account types.

Auth error codes

CodeStatusWhat to do
AUTH_NOT_SIGNED_IN401Call sdk.signIn().
AUTH_CHALLENGE_FAILED500Check publishable key and network. Retry.
AUTH_VERIFICATION_FAILED401User may have cancelled signing or wallet address mismatch.
AUTH_TOKEN_EXPIRED401JWT expired. The SDK auto-refreshes, but call signIn() if it persists.
AUTH_INVALID_SESSION400Malformed data in restoreSession(). Clear and sign in again.

Accounts

Resolution

How Blend maps wallet addresses to accounts differs by SDK. Frontend (automatic): The publishable key identifies your organization and account type. SIWE proves wallet ownership. After sign-in, the account is implicit. Server (manual): The API key identifies your organization. The accountTypeId in the SDK config selects the product. You call sdk.lookupAccount(address) to find or create the account. You call sdk.forAccount(accountId) to scope operations.

Safe submodule

Check and trigger Safe deployments on specific chains.
// Check if Safe is deployed on a chain
const result = await sdk.account.safe.resolve(8453);
resolve returns a discriminated union:
StatusMeaning
"validated"Safe exists on this chain. Returns accountId, userAddress, safeAddress, chainId.
"not-deployed"Safe hasn’t been deployed on this chain yet.
"invalid"Contract at the address is not a valid Safe.
"disconnected"Could not reach the chain RPC.
// Request deployment on a new chain
await sdk.account.safe.request(8453);
request is fire-and-forget. Deployment happens in the background.

Balance & positions

balance

Current aggregate balance with per-chain breakdown.
const balance = await sdk.account.balance();
FieldTypeDescription
accountIdstringBlend account ID
safeAddressstringOn-chain Safe address
perChainBalancePerChain[]Per-chain vault breakdown with fiat values
totalFiatAmountAggregate balance across all chains
heldAssetsHeldAsset[]Assets currently held in the Safe

balanceHistory

Time-series balance snapshots. Use startDate and endDate to filter.
const history = await sdk.account.balanceHistory({
  startDate: "2025-01-01",
  endDate: "2025-03-01",
});

positions

All position events (deposits, withdrawals, rebalances) sorted newest first.
const positions = await sdk.account.positions();
Returns deposit, withdrawal, and rebalance events with transaction hashes, amounts, and timestamps.

returns

Profit and loss metrics for the account.
const returns = await sdk.account.returns();
FieldTypeDescription
currentFiatAmountCurrent balance value
totalDepositedFiatAmountSum of all deposits
totalWithdrawnFiatAmountSum of all withdrawals
netDepositedFiatAmounttotalDeposited - totalWithdrawn
returnsFiatAmountcurrent - netDeposited
returnsPctnumberreturns / netDeposited * 100

Yield

Account-type-level yield data. Not per-account.
const yieldData = await sdk.discover.yield();
FieldTypeDescription
accountTypeIdstringThe account type
yieldBreakdownChainYieldBreakdown[]Per-chain APY breakdown with base and boosted rates

Error handling

SdkError

All SDK methods throw SdkError on failure.
import { SdkError } from "@blend-money/core";

try {
  await sdk.quoteDeposit({ /* ... */ });
} catch (error) {
  if (error instanceof SdkError) {
    error.status;           // HTTP status code (0 for network)
    error.code;             // machine-readable code string
    error.message;          // human-readable description
    error.response;         // raw API response body
    error.isRetryable();    // true for 429, 5xx, network errors
    error.getUserMessage(); // user-friendly message
  }
}

Error codes

Session errors:
CodeStatusMeaning
FLOWPLAN_CONFLICT409Rebalance in progress. Retry after it settles.
INTENT_NOT_FOUND404Session UUID not found.
INTENT_EXPIRED410Quote has expired. Re-quote.
INTENT_WRONG_STATUS409Session cannot be modified in current state.
INTENT_CONCURRENT_TRANSITION409Session status changed concurrently. Retry.
SESSION_NOT_QUOTED409Must quote before locking or executing.
SESSION_LOCKED_BY_OTHER409Locked by a different signer address.
SETTLEMENT_TIMEOUT408Polling exceeded timeout (default 5 min).
Auth errors:
CodeStatusMeaning
AUTH_NOT_SIGNED_IN401Not authenticated. Call signIn().
AUTH_CHALLENGE_FAILED500SIWE challenge request failed. Check publishable key.
AUTH_VERIFICATION_FAILED401Wallet signature verification failed.
AUTH_TOKEN_EXPIRED401JWT expired. Auto-refresh will attempt re-auth.
AUTH_INVALID_SESSION400Malformed session data in restoreSession().
General errors:
CodeStatusMeaning
NETWORK_ERROR0No response from server.
VALIDATION_ERROR400Client-side validation failed.
RATE_LIMITED / RATE_LIMIT429Too many requests.
NOT_FOUND404Resource not found.
TIMEOUT408Request timed out.
SERVER_ERROR500Internal server error.
NOT_IMPLEMENTED501Feature not available.

Amount utilities

Convert between human-readable amounts and smallest-unit strings.
import { parseAmount, formatAmount } from "@blend-money/core";

parseAmount("100.50", 6);   // "100500000"  (USDC: 6 decimals)
parseAmount("1.5", 18);     // "1500000000000000000"  (18 decimals)

formatAmount("100500000", 6);           // "100.5"
formatAmount("1500000000000000000", 18); // "1.5"
parseAmount rejects fractional digits exceeding the token’s decimal count.

Key types

SessionStatus

type SessionStatus = "OPEN" | "LOCKED" | "SUBMITTED" | "SETTLED" | "FAILED" | "CANCELLED";
Terminal states: SETTLED, FAILED, CANCELLED.

DepositQuote

type DepositQuote = {
  readonly type: "DEPOSIT";
  readonly intentId: string;
  readonly originChainId: number;
  readonly destinationChainId: number;
  readonly input: { readonly symbol: string; readonly amount: string; readonly amountUsd: string };
  readonly output: { readonly symbol: string; readonly amount: string; readonly amountUsd: string };
  readonly fees: { readonly totalUsd: string };
  readonly estimatedSeconds: number;
  readonly expiresAt: string;
};

WithdrawQuote

type WithdrawQuote = {
  readonly type: "WITHDRAW";
  readonly intentId: string;
  readonly destinationChainId: number;
  readonly totalAmount: string;
  readonly totalFeesUsd: string;
  readonly estimatedSeconds: number;
  readonly sourceChainCount: number;
  readonly sourceChainIds: readonly number[];
  readonly expiresAt: string;
};

ExecuteResult

type ExecuteResult = {
  status: "settled" | "failed" | "cancelled";
  txHashes: Array<{ hash: string; chainId: number }>;
  settledAt: string | null;
  error: string | null;
};

ActionPlan

type ActionPlan = {
  deployType: "direct" | "multisend";
  requiredApprovals: Txn[];
  requiredTxns: Txn[];
  chainId: number;
};
Deposits produce a single ActionPlan with deployType: "direct". Withdrawals produce an ActionPlan[] with deployType: "multisend".

BalanceResponse

type BalanceResponse = {
  accountId: string;
  safeAddress: string;
  perChain: BalancePerChain[];
  total: FiatAmount;
  heldAssets: HeldAsset[];
};

Frontend SDK

Frontend configuration, auth, and execution.

Server SDK

Server configuration, account management, and sessions.
Last modified on May 7, 2026