Skip to content
LogoLogo

For Frontends

Use the Liquid Lane API to redeem supported RWAs for USDC in your app. Connect the user's wallet, request approval when needed, and sign an EIP-712 order.

NetworkBase URL
Mainnethttps://swap.symbiotic.fi/api/v1
Hoodihttps://swap.hoodi.symbiotic.fi/api/v1
Sepoliahttps://swap.sepolia.symbiotic.fi/api/v1

Use the matching chain and Reactor address. See the API reference for full schemas.

Integration Steps

YesNoYesNoCheck allowancePOST /check_approvalApprovalrequired?Approve ReactorWait for receiptRequest quotePOST /quoteQuoteavailable?No quoteuser changes the amountSign requestEIP-712 typed dataSubmit orderPOST /orderTrack orderGET /ordersfilledexpirederror

Request a quote

Call POST /quote with the tokens, amount, wallet (swapper), and output recipients. Use type: "EXACT_INPUT", set both chain IDs to the deployment chain (1 for mainnet), and set each output's token to tokenOut.

The response contains quotes, sorted best-first. Each entry includes solver, quote, and signatureData. Choose quotes[0] for the best quote. Display the user's amount from the selected quote.orderInfo.outputs, formatted with the token's decimals. Refresh quotes while the user reviews them.

  • 404 means no quote is available. Let the user change the amount or try again.

Approve the Reactor

Call POST /check_approval with { walletAddress, chainId, token, amount }. If approval is null, continue to signing. Otherwise, send its { to, data, value } transaction and wait for a successful receipt. It grants the Reactor an unlimited ERC-20 allowance.

Sign

Sign signatureData as EIP-712 typed data with primaryType: "Request", passing value as the message.

Submit the order

Call POST /order with { quote, signature }, keeping the quote unchanged. Persist the returned orderId before polling. Retrying the same quote and signature returns the existing order.

  • 404 means the quote is unknown.
  • 409 means it expired or no solver can honor it. Request a new quote and signature.

Track settlement

Poll GET /orders?orderId=<orderId> every few seconds.

orderStatusAction
openKeep polling.
filledShow txHash and settledAmounts.
expired, error, cancelled, insufficient-fundsOffer a new quote.

Bound each request and the polling window. A timeout does not cancel an order: retain its ID and resume tracking after reload.

Examples

const API = "https://swap.symbiotic.fi/api/v1";
 
async function post(path: string, body: object) {
    const response = await fetch(`${API}${path}`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
        signal: AbortSignal.timeout(15_000),
    });
    if (!response.ok) throw new Error(`${path}: HTTP ${response.status}`);
    return response.json();
}
 
const [swapper] = await walletClient.getAddresses();
 
// Quote
const { quotes } = await post("/quote", {
    tokenInChainId: 1,
    tokenOutChainId: 1,
    tokenIn,
    tokenOut,
    amount,
    type: "EXACT_INPUT",
    swapper,
    outputs: [{ token: tokenOut, recipient: swapper }],
});
 
// Choose the best quote (highest output)
const { quote, signatureData } = quotes[0];
 
// Approve the Reactor if needed
const { approval } = await post("/check_approval", {
    walletAddress: swapper,
    chainId: 1,
    token: tokenIn,
    amount,
});
if (approval) {
    const hash = await walletClient.sendTransaction({
        account: swapper,
        ...approval,
        value: BigInt(approval.value),
    });
    const receipt = await publicClient.waitForTransactionReceipt({ hash });
    if (receipt.status !== "success") throw new Error("Approval reverted.");
}
 
// Sign
if (quote.orderInfo.deadline <= Date.now() / 1000)
    throw new Error("Quote expired. Request a new quote.");
const { domain, types, primaryType, value } = signatureData;
const signature = await walletClient.signTypedData({
    account: swapper,
    domain,
    types,
    primaryType,
    message: value,
});
 
// Submit; save orderId to track settlement
const { orderId } = await post("/order", { quote, signature });

Fees

Add an output with your fee recipient and portionBps (25 = 0.25%). Exactly one output must omit portionBps to receive the remainder.

{
    "outputs": [
        { "token": "0xUsdc" },
        { "token": "0xUsdc", "recipient": "0xYourFeeWallet", "portionBps": 25 }
    ]
}

Solvers

Each quotes[] entry includes solver metadata: use solver.name for the display name and solver.metadata.logoUrl for the logo when present.

Solver IDNameNetwork
symbiotic_firstSymbioticMainnet
symbiotic_secondKeyrockMainnet
symbiotic_thirdKPKMainnet
symbiotic_fifthClearstarMainnet

Liquidity Preview

Call POST /liquidity with { tokenIn } and optional solverIds to preview depth. Each levels[] entry includes an input amount and a bestQuote, or null if no solver quoted that size. bestQuote.priceImpactBps measures the shortfall from the reference price (50 = 0.50%).

Use these levels as estimates. Request a /quote for the user's chosen amount before signing.

API Errors

Error responses contain { error: { code, message, status, timestamp, details? } }; validation errors include details.issues[]. Log the requestId from successful responses for support.