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 }. The backend returns unsigned { to, data, value } payloads for the wallet to sign and submit:

  • Sufficient allowance: cancel and approval are both null; continue to signing.
  • Zero allowance: send only approval, which grants the Reactor an unlimited ERC-20 allowance.
  • Nonzero but insufficient allowance: send cancel first to reset the Reactor's allowance to zero, then send approval.

Wait for each transaction to succeed before sending the next one or signing the order. Wallets with atomic batching support can execute [cancel, approval] together in that order. cancel resets the token allowance; it does not cancel an order.

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

async function (: string, : object) {
    const  = "https://swap.symbiotic.fi/api/v1";
    const  = await (`${}${}`, {
        : "POST",
        : { "Content-Type": "application/json" },
        : .(),
        : .(15_000),
    });
    if (!.) throw new (`${}: HTTP ${.}`);
    return .();
}
 
import type {  } from "viem";
 
const [] = await .();
 
// Quote
const {  } = await ("/quote", {
    : 1,
    : 1,
    ,
    ,
    ,
    : "EXACT_INPUT",
    ,
    : [{ : , :  }],
});
 
// Choose the best quote (highest output)
const { ,  } = [0];
 
// Approve the Reactor if needed
const { ,  } = await ("/check_approval", {
    : ,
    : 1,
    : ,
    ,
});
for (const  of [, ]) {
    if (!) continue;
    const  = await .({
        : ,
        ...,
        : (.),
    });
    const  = await .({  });
    if (. !== "success") throw new ("Approval reverted.");
}
 
// Sign
if (.. <= .() / 1000)
    throw new ("Quote expired. Request a new quote.");
const { , , ,  } = ;
const  = await .({
    : ,
    ,
    :  as unknown as ,
    ,
    : ,
});
 
// Submit; save orderId to track settlement
const {  } = await ("/order", { ,  });

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.

The following solvers are configured on Mainnet:

Solver IDNameNetwork
symbiotic_symbioticSymbioticMainnet
symbiotic_keyrockKeyrockMainnet
symbiotic_kpkKPKMainnet
symbiotic_cassaCassaMainnet
symbiotic_clearstarClearstarMainnet
symbiotic_prestoPrestoMainnet

symbiotic_infinifi (InfiniFi) and symbiotic_bizantine (Bizantine) are reserved IDs and are not currently configured on Mainnet.

Use the solver IDs above in quote and liquidity filters.

Liquidity

Call POST /liquidity with { tokenIn } and optional solverId or solverIds. The response includes tokenIn, tokenOut, their metadata (tokenInInfo, tokenOutInfo), and these liquidity fields:

{
    "totalLiquidity": "2500000000",
    "solvers": [
        { "solverId": "symbiotic_symbiotic", "liquidity": "2000000000" },
        { "solverId": "symbiotic_keyrock", "liquidity": "1000000000" }
    ]
}

Each solver's liquidity is the sum of the effective adapters[].maxAssets available to it during quote construction, after authorization and live-discount validation. The solvers array is sorted by liquidity from highest to lowest.

totalLiquidity is not the sum of the solver values. It counts the shared internal inventory once, then adds each external solver's liquidity, so it can differ from the sum in either direction:

  • Lower when solvers share adapters. In the example above, 500 tokens of inventory back both solvers and count once, so the total is 2,500 rather than 3,000.
  • Higher when eligible internal inventory is not bound to any returned solver. Internal solvers still receive that inventory in quote requests, but it has no solver row.

The total is never below any single solver's liquidity. Validate each value on its own and don't require the solver values to add up to the total.

Pass solverId: "symbiotic_keyrock" to restrict both the list and totalLiquidity to that solver. In the example above, the list then contains only symbiotic_keyrock and the total becomes "1000000000". If that solver has no eligible liquidity, the total is "0" and the list is empty. The solverIds array filters both the list and the total when solverId is omitted. If both are supplied, solverId takes precedence.

Amounts are integer strings in tokenOut base units. Format them with tokenOutInfo.decimals: the example total is 2,500 tokens when decimals is 6. Use BigInt for sums and comparisons.

Read several assets at once

To read several input tokens in one request, call POST /liquidity-batch with tokensIn and the same optional solverId or solverIds:

{ "tokensIn": ["0x…", "0x…"] }
  • tokensIn takes 1 to 20 distinct addresses. Duplicates are compared case-insensitively and rejected with HTTP 400.
  • The response is { tokensIn: [...] }, holding the /liquidity snapshot shown above for each token, in request order.
  • solverId and solverIds apply to every token.
  • If any token is unsupported, the whole request fails with HTTP 400 before any inventory is read, and the error details name that token.

POST /liquidity stays single-token: it takes tokenIn and returns one snapshot.

Show liquidity in a frontend

Use totalLiquidity to sort assets, loading it in the background with one /liquidity-batch request for the whole asset list. For Capacity: <capacity>, match the selected quote's solver.id to solvers[].solverId and display that solver's liquidity. Solvers without eligible inventory are omitted. Empty inventory returns HTTP 200 with totalLiquidity: "0" and solvers: [].

This response replaces the sampled levels and price-impact fields. It reads inventory without requesting solver quotes and does not reserve funds. Request a fresh /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[].

For support, record the HTTP status and X-Request-Id response header on both successful and failed requests. The backend exposes this header through CORS. Treat the ID as an opaque string.

After collecting wallet signatures for a discount, submit the signed terms to POST /discount and wait for HTTP 200 before marking it published. Publication and cancellation return { discountId }. Read request correlation from the header, which identifies the HTTP attempt and is not an idempotency key.