0.14.0, served by https://composer.li.quest.
A server-side change applies to every client the day it ships. The SDK version next to it is the first release whose types match. To upgrade, install both packages together (see Before you start):
Breaking changes
SDK 0.14.0 (October 26, 2026)
paraswap.buy returns all of its unsold input as unspentIn.
unspentIn is now the full input minus the amount sold: 1,050 USDC in, 1,000 USDC sold, 50 USDC on unspentIn. Before, input above the op’s quote-based cap stayed on the execution proxy with no port naming it.
- Migrate: recompile stored
paraswap.buyflows instead of replaying their calldata, and re-check any downstream sizing based on the oldunspentIn.
SDK 0.13.0 (October 22, 2026)
A settle node may commit zero outcomes. Acontinuation.settle node with exact: true may now set outcomes: []: the leg itself is the only thing the settlement enforces. Such a node has no <nodeId>.output entry in outputs.
- Migrate: code that reads
config.outcomes[0]oroutputs['<settleId>.output']must handle the empty case. See Cross-chain flows.
SDK 0.12.0 (October 21, 2026)
Composer fee collection is removed.run.integratorFeeBps no longer exists; a request that sends it fails with 400 VALIDATION_ERROR. The fees response field, the ComposeFee type, and the fee_resolution_error and fee_resolution_unavailable error kinds are gone.
- Migrate: delete
integratorFeeBpsfrom every compile request and stop readingresult.fees.
config.perpetual; sending it fails with 400 decode_error. Every compile registers a fresh, one-time escrow.
- Migrate: remove
perpetualfrom settle configs. Never reuse an escrow address from an earlier compile; read it fromsettlements[nodeId].escroweach time.
verificationByNode[*].assertedPredicate changed shape.
The erc20BalanceDeltaGte and nativeBalanceDeltaGte predicates were removed, and the call predicate was reshaped. SDK 0.5.x to 0.11.x clients receive the new shape without matching types.
- Migrate: if you read
verificationByNode, upgrade to SDK 0.12.0 or later and re-check your parsing.
SDK 0.11.3 (October 16, 2026)
run.sweepTo is shorthand for the flow epilogue, and the SweepTo type is renamed ForwardTo.
A flow can now carry an epilogue (named forwards plus a remainder address); run.sweepTo sets epilogue.remainder. The wire shape of sweepTo is unchanged.
- Migrate: replace
import type { SweepTo }from@lifi/compose-specwithForwardTo. Send eitherrun.sweepToorflow.epilogue, not both; a request with both fails withvalidation_erroratrun.sweepTo. See Sweeping and amounts.
outputs under the key input.<name>, with role: 'terminal' and amount.minimum.basis: 'input'.
- Migrate: code that iterates
outputsmust expectinput.*keys next to<nodeId>.<portName>keys.
outcomes fails with validation_error. Inside the leg (continuationFlow), binding context.sender fails with reason leg_binds_sender, because in a leg it is the settlement service’s executor, not your user. A leg that declares its own epilogue fails with reason leg_names_epilogue.
- Migrate: split multi-outcome settles into one settle node per outcome. Where the leg needs a receiver, bind
context.executionAddress; the compiler writes the leg’s epilogue from the settle’s outcome andrefund. See the cross-chain flow guide.
SDK 0.10.0 (October 13, 2026)
A settle node’s funding needs an enforced minimum. The amount bound to a settle node’sinput port must carry a committed floor (amount.minimum) that the chain enforces. Funding with no floor, or a floor of 0, fails with validation_error naming the settle node and its input port, before any escrow is registered.
- Migrate: fund the settle from a declared input deposited with a fixed
amount, from a quoted output that provides a minimum (such aslifi.swap’samountOut), or add aslippageguard on the producing port. AdirectDepositwithallowNonExactcannot fund a settle directly.
SDK 0.9.0 (October 12, 2026)
core.transfer moves its whole input; amount and remainder are removed.
core.transfer now sends everything bound to its amount port and has a single output, transferred. An old config: { amount } fails with decode_error.
- Migrate: to send a fixed amount, split it off with
core.splitAtfirst, then transfer theheadoutput. See Sweeping and amounts.
lifi.swap slippage must be between 0 and 1.
slippage is a fraction: 0.03 means 3%. Before, slippage: 30 was sent to the quote as 3000%. A value outside [0, 1] now fails with decode_error. The default stays 0.03.
- Migrate: express slippage as a fraction, for example
0.005for 0.5%. Seelifi.swap.
503 resolution_error.
When a quote, routing or settlement service is temporarily unavailable, the compile fails with 503 resolution_error instead of 422 preparation_error.
- Migrate: retry
503 resolution_errorwith backoff; keep treating422kinds as failures of the request or flow.
SDK 0.8.1 (October 8, 2026)
The manual-relay settlement mode is removed. Thecontinuation.assertReady op and the settlements[nodeId].continuation response field are gone. A settle node that names no provider uses LI.FI’s settlement service, which fills the escrow.
- Migrate: remove
continuation.assertReadynodes and stop relaying legs yourself. Track progress with the escrow status endpoint.
October 5, 2026: new execution contracts (VM v1.2)
Every signer has a new execution proxy. Production moved to new Composer contracts, so every signer’s execution proxy has a new address. Approvals and balances on the old proxy stay there; the signer can still use them on chain, but the API no longer targets the old proxy.- Migrate: do not cache proxy or spender addresses. Read
userProxy,approvalsandactionsfrom each compile response; your users grant approvals to the new proxy once. Current addresses are on Addresses.
SDK 0.7.0 (October 2, 2026)
ERC-20 token addresses are validated when the flow is decoded. A resource’stoken must be a 0x-prefixed 20-byte address. Placeholders such as '0xUSDC' fail with 400 decode_error. Settle node addresses, integer strings and expiries are validated the same way.
- Migrate: replace placeholder tokens with real addresses.
delivery.when: 'now') must be on the flow’s chainId; otherwise the compile fails with validation_error.
- Migrate: use
lifi.bridgefor cross-chain moves. See its example.
phase1ByNode and continuationPlans are replaced by settlements.
The compile response returns one record per settle node, settlements[nodeId], with the escrow terms under escrow. Each fundEscrow action names its settle node in nodeId.
- Migrate: read escrow details from
settlements[nodeId].escrowand submitactionsin order. See the cross-chain flow guide.
SDK 0.6.0 (September 14, 2026)
outputs replaces producedResources.
The compile response has one outputs record with an entry per port, keyed <nodeId>.<portName>, carrying role, resource and amount: { estimate?, minimum? }. See Flow Schema: outputs.
- Migrate: read
outputsinstead ofproducedResources, and useterminalOutputs()to keep only what the flow hands back. The deprecatedproducedResourcesfield is still returned for 0.5.x clients and will be removed; don’t rely on it.
{ $ref } objects.
TypedRef is now branded, so a hand-written { $ref: 'node.port' } no longer satisfies a typed bind slot.
- Migrate: take refs from typed handles, or build them with
raw.ref<T>().
destination bind is removed.
bind.destination on a continuation.settle node now fails with 400 validation_error (Extra bind key "destination").
- Migrate: set the receiver only in
config.outcomes[0].destination.
SDK 0.5.1 (September 6, 2026)
SDKs older than 0.5.0 are rejected. The API now checks the SDK version onPOST /compose, POST /compose/route and POST /compose/sweep. Older SDKs get 400 sdk_outdated; see Compatibility policy.
- Migrate: upgrade both packages to the same version,
0.14.0recommended.
SDK 0.5.0 (September 3, 2026)
The compile result was reshaped.-
spendsandquote.validUntilMsmoved underfill: { spends, validUntilMs? }.fill.validUntilMsis absent when nothing in the flow expires. -
Each
spendsentry gainedinput,chainIdand an optionalnodeIdnext totokenandamount. -
transactionRequestis optional on success: readactionsfor the ordered list of transactions to submit. -
verificationDescriptorbecameverificationByNode, keyed by node id. -
The
continuationsarray was removed. -
Migrate: read
fill.spendsandfill.validUntilMsinstead ofspendsandquote.validUntilMs, and handle a missingtransactionRequest.
SDK 0.4.0 (August 21, 2026)
quote.validUntilMs is the real expiry.
Before, it was always a far-future placeholder. It is now the earliest expiry of any prepared node; the placeholder remains only when nothing in the flow expires.
- Migrate: treat the expiry as a deadline: submit before it, or recompile. From SDK 0.5.0 the field is
fill.validUntilMs.
SDK 0.3.0 (August 14, 2026)
apiKey is required.
createComposeSdk({ baseUrl }) no longer type-checks, and a missing or blank key throws ComposeError with code VALIDATION_ERROR when the SDK is created. The API rejects requests without a key with 401.
- Migrate: pass
apiKeytocreateComposeSdk. See Before you start.
SDK 0.2.0 (June 23, 2026)
The legacyPOST /route and GET /zap-packs endpoints are removed.
- Migrate: use
POST /compose/route(sdk.route) andGET /compose/zap-packs.
Notable additions
See the SDK API reference, the op catalog and Errors for the full surface.
Compatibility policy
The API serves every SDK from a floor up to its own version. The SDK sends its version in thex-lifi-composer-sdk request header, and the API checks it on POST /compose, POST /compose/route and POST /compose/sweep. @lifi/compose-spec is a peer dependency pinned to the same version as @lifi/composer-sdk, so upgrade them together.
Both kinds reach the SDK as a
ComposeError with code VALIDATION_ERROR; narrow them with isComposeSdkOutdatedError and isComposeServerOutdatedError (see SDK reference: errors).
To see which version production serves, read the x-lifi-composer-version header of GET /compose/manifest, with a unique query parameter:

