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.

A working SDK integration is step one. A production-quality integration handles edge cases, secures credentials, and recovers from failures.

Credential security

Your sk_live_ API key grants full access to your organization’s accounts. Treat it like a database password.
import { BlendServerSdk } from "@blend-money/node";

// Load from environment, never hardcode
const sdk = new BlendServerSdk({
  apiKey: process.env.BLEND_API_KEY!,
  accountTypeId: "your-account-type-id",
});
  • Store API keys in environment variables or a secrets manager
  • Never log the full key value
  • Create separate keys per environment (dev, staging, production)
  • Rotate keys on a regular schedule, not only after incidents
Your publishable key (pk_live_) is safe for client-side code. It identifies your account type but cannot read or modify account data on its own. The SIWE session provides the authorization layer.
Rotating your organization’s signing key invalidates all active SIWE sessions. Plan key rotation during low-traffic windows.

Session management

Deposits and withdrawals run through stateful sessions. One active session per account at a time. Re-quoting: Call quoteDeposit or quoteWithdraw again on the same OPEN session to update amounts and prices. Do not create a new session for re-quotes.
// Re-quote on the same session (correct)
const quote1 = await sdk.quoteDeposit({
  chainId: 8453,
  tokenAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  amount: "1000000",
});

// User changes amount, re-quote same session
const quote2 = await sdk.quoteDeposit({
  chainId: 8453,
  tokenAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  amount: "5000000",
});
Force reset: Use forceReset: true only when you need to cancel the existing session entirely. This is for starting a completely new flow, not for updating a quote. Session expiration TTLs:
StateTTLOn expiry
OPEN~15 minutesAuto-cancelled
LOCKED~1 hourAuto-cancelled
SUBMITTED~1 hourMarked as failed
Build your UI to show quote expiration and allow re-quoting while the session is OPEN.

Transaction handling

Wait between action plans. The SDK calls waitForNextBlock() between sequential action plans. If you build custom execution logic, do the same. Submitting two plans in the same block can cause nonce collisions. Multi-chain withdrawal sequencing. Withdrawals can produce multiple action plans (one per source chain). Process them in sequence, not in parallel.
import { BlendServerSdk } from "@blend-money/node";

const sdk = new BlendServerSdk({
  apiKey: process.env.BLEND_API_KEY!,
  accountTypeId: "your-account-type-id",
});

const client = sdk.forAccount(accountId);

// execute() handles sequencing automatically
const session = await client.sessions.execute(intentId, {
  signerAddress: eoa,
  submitActionPlan: async (plan) => {
    // Your on-chain execution logic here
    return [{ hash: txHash, chainId: plan.chainId }];
  },
});
Paymaster sponsorship. Both SDKs require an ERC-4337 paymaster for gas-sponsored withdrawals. Configure paymasterUrl with a chain-aware resolver.
import { getPimlicoPaymasterUrl } from "@blend-money/core";

const paymasterUrl = getPimlicoPaymasterUrl("your-pimlico-api-key");
// Returns: (chainId) => `https://api.pimlico.io/v2/${chainId}/rpc?apikey=...`

Conflict handling

A 409 with FLOWPLAN_CONFLICT means a rebalance is in progress on the account. The system is moving funds between vaults. Do not retry blindly. Surface this as a product-level message and let the user retry after the account settles.
import { SdkError } from "@blend-money/core";

try {
  const quote = await sdk.quoteWithdraw({
    destinationChainId: 8453,
    amount: "1000000",
  });
} catch (error) {
  if (error instanceof SdkError && error.code === "FLOWPLAN_CONFLICT") {
    // Show: "Your account is being rebalanced. Try again in a few minutes."
    return;
  }
  throw error;
}
Flow plan conflicts only affect withdrawals. Deposits are not blocked by active rebalances.

Safe deployment

Safes are deployed lazily. A resolved Blend account does not mean the Safe exists on every chain. Before first execution on a chain, check deployment state.
const resolved = await sdk.account.safe.resolve(8453);

if (resolved.status === "not-deployed") {
  await sdk.account.safe.request(8453);
  // Wait for deployment, then retry resolve
}
Your Safe has the same address on every chain. Once deployed on one chain, you can deploy on any other chain and get the same address. The CREATE2 determinism makes cross-chain reuse automatic.

Error recovery

The execute method in both SDKs is crash-safe. If your app crashes mid-execution, calling execute again with the same intent resumes from the current session state. It does not restart from scratch.
import { SdkError } from "@blend-money/core";

try {
  await sdk.execute(quote, {
    signerAddress: eoa,
    deriveSigner: async (chainId) => ({
      signer: getWalletClient({ chainId }),
      publicClient: getPublicClient({ chainId }),
    }),
  });
} catch (error) {
  if (error instanceof SdkError && error.isRetryable()) {
    // Safe to retry: 429, 5xx, network errors
    // Exponential backoff built into HTTP client
  }
}
isRetryable() returns true for HTTP 429, 5xx status codes, and network errors (status 0). The built-in HTTP client already retries with exponential backoff and jitter, so you only need manual retry logic for application-level recovery.

Frontend SDK

See the full frontend SDK reference.

Server SDK

See the full server SDK reference.
Last modified on May 7, 2026