The swap `id` and the `receiver` are assigned by Layerswap — use the exact values from the deposit
diff --git a/api-reference/gasless-swaps.mdx b/api-reference/gasless-swaps.mdx
index c9a3547..e27e026 100644
--- a/api-reference/gasless-swaps.mdx
+++ b/api-reference/gasless-swaps.mdx
@@ -1,7 +1,6 @@
---
title: "Gasless Swaps"
description: "Let users fund a swap by signing an off-chain message instead of sending an on-chain transaction — Layerswap's paymaster broadcasts the deposit and pays the gas."
-icon: signature
---
## Overview
diff --git a/api-reference/refunds.mdx b/api-reference/refunds.mdx
index a94ba53..ef253da 100644
--- a/api-reference/refunds.mdx
+++ b/api-reference/refunds.mdx
@@ -1,7 +1,6 @@
---
title: 'Refunds'
description: 'Learn how Layerswap handles refunds'
-icon: arrow-rotate-left
---
When a swap cannot be completed after the user has sent funds, Layerswap automatically initiates a refund. The refund is always processed on the **source chain** in the **source token**. Gas fees for processing the refund transaction are deducted from the refund amount.
diff --git a/api-reference/swap-lifecycle.mdx b/api-reference/swap-lifecycle.mdx
index aff7d44..055335f 100644
--- a/api-reference/swap-lifecycle.mdx
+++ b/api-reference/swap-lifecycle.mdx
@@ -1,7 +1,7 @@
---
title: 'Swap lifecycle'
+sidebarTitle: 'Overview'
description: 'Understand the different statuses a swap goes through from creation to completion or refund.'
-icon: arrows-spin
---
diff --git a/api-reference/webhook.mdx b/api-reference/webhook.mdx
index cf73edd..2508bb4 100644
--- a/api-reference/webhook.mdx
+++ b/api-reference/webhook.mdx
@@ -1,7 +1,6 @@
---
title: 'Webhooks'
description: 'Describes the mainnet and tesnet enviorments'
-icon: webhook
---
### Setup
diff --git a/docs.json b/docs.json
index 83aeff6..66c452a 100644
--- a/docs.json
+++ b/docs.json
@@ -131,11 +131,34 @@
"group": "Documentation",
"pages": [
"integration/API",
- "api-reference/depository",
- "api-reference/gasless-swaps",
+ {
+ "group": "Sending the deposit",
+ "pages": [
+ "api-reference/deposit-actions/overview",
+ {
+ "group": "Deposit methods",
+ "pages": [
+ "api-reference/deposit-actions/choosing-a-method",
+ "api-reference/depository",
+ "api-reference/gasless-swaps"
+ ]
+ },
+ {
+ "group": "By network",
+ "pages": [
+ "api-reference/deposit-actions/evm",
+ "api-reference/deposit-actions/bitcoin",
+ "api-reference/deposit-actions/solana",
+ "api-reference/deposit-actions/starknet",
+ "api-reference/deposit-actions/ton",
+ "api-reference/deposit-actions/tron",
+ "api-reference/deposit-actions/fuel"
+ ]
+ }
+ ]
+ },
{
"group": "Swap lifecycle",
- "icon": "arrows-spin",
"pages": [
"api-reference/swap-lifecycle",
"api-reference/refunds"
diff --git a/integration/API.mdx b/integration/API.mdx
index 47514b3..77a2eb5 100644
--- a/integration/API.mdx
+++ b/integration/API.mdx
@@ -1,34 +1,165 @@
---
title: 'API Integration'
-description: ''
-icon: server
+description: 'Bridge tokens across networks with the Layerswap API — quote a route, create a swap, send the deposit, and track it to completion.'
---
## Overview
-Layerswap API is set to enable fast and reliable crypto token swaps across networks
-
-Layerswap handles millions of dollars in transactions every day across multiple networks. We ensure quick and reliable crypto transfers, giving the freedom to move crypto anywhere.
-
-## Quickstart
-
-
-
- Get supported sources/networks, available routes and swap fees.
-
-
- Create Swap using the Layerswap API.
-
-
- Layerswap will monitor the source network for a transaction and will try to match it with the corresponding swap
-
-
- Once the transaction is matched and added to the swap, Layerswap will initiate a counterparty transaction to the destination_address
-
-
- Poll via Get Swap endpoint to see if the matching transaction Input was added to the swap
-
-
- If the webhook is configured, Layerswap will deliver a swap status update notification to the partner-specified URL
-
-
\ No newline at end of file
+Layerswap bridges tokens across networks through a REST API: you **quote** a route, **create** a swap,
+submit one **deposit** transaction on the source network, and **track** it while Layerswap delivers on the
+destination.
+
+- **Base URL:** `https://api.layerswap.io`
+- **API key (optional):** send it in the `X-LS-APIKEY` header to attribute swaps to your account and get
+ higher rate limits. Create one on the [API Keys](/api-keys) page.
+
+## 1. Get a quote
+
+Start with a quote for the exact route and amount you want. It returns the amount you'll receive, the
+fees, and the estimated time — and tells you whether the route is supported.
+
+[`GET /api/v2/quote`](/api-reference/swaps/get-quote) — pass the source and destination network and token,
+plus an `amount`:
+
+```json
+{
+ "data": {
+ "quote": {
+ "receive_amount": 99.5,
+ "min_receive_amount": 98.5,
+ "total_fee": 0.5,
+ "total_fee_in_usd": 0.5,
+ "blockchain_fee": 0.15,
+ "service_fee": 0.35,
+ "avg_completion_time": "00:02:00",
+ "rate": 0.995,
+ "slippage": 0.5
+ }
+ }
+}
+```
+
+
+ **Identify tokens by contract address, not a hard-coded symbol.** A symbol is a display label and can
+ change — Arbitrum's USDT is now `USDT0`, so a hard-coded `"source_token": "USDT"` silently stops
+ matching. Pass the **contract address**, or look the token up from `/sources`
+ ([see below](#discovering-routes--tokens)). Don't hard-code symbols or decimals — read them from the API.
+
+
+See [Fees](/fees) for how the fee fields are composed.
+
+## 2. Create the swap
+
+[`POST /api/v2/swaps`](/api-reference/swaps/create-swap) creates the swap and returns the `deposit_actions`
+you'll send next. Choose **one** funding method:
+
+| Funding method | Set | Best for |
+| --- | --- | --- |
+| **Direct transfer** (default) | neither flag | A user's own wallet (EOA) |
+| **Deposit address** | `use_deposit_address: true` | Exchanges, custodial flows, or paying from anywhere |
+| **Depository contract** | `use_depository: true` | Aggregators, and apps that fund from a smart contract — see [Depository](/api-reference/depository) |
+
+`use_depository` and `use_deposit_address` are mutually exclusive.
+
+```bash
+curl -X POST https://api.layerswap.io/api/v2/swaps \
+ -H "X-LS-APIKEY: $LAYERSWAP_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "source_network": "ARBITRUM_MAINNET",
+ "source_token": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
+ "destination_network": "BASE_MAINNET",
+ "destination_token": "USDC",
+ "destination_address": "0xRecipient",
+ "amount": 100,
+ "use_depository": true
+ }'
+```
+
+The response includes the created `swap` (its `id` and `status`), the `quote`, and the `deposit_actions`.
+
+## 3. Send the deposit
+
+Each **deposit action** in the response is a ready-to-send transaction for the source network. Read it
+from the swap response (or re-fetch via
+[`GET /api/v2/swaps/{swapId}/deposit_actions`](/api-reference/swaps/get-deposit-actions)):
+
+```js
+const action = swap.deposit_actions[0];
+// action.to_address, action.amount_in_base_units, action.call_data, action.network, action.token
+```
+
+How you submit it depends on the source network — the `call_data` is EVM calldata on one network, a
+Bitcoin `OP_RETURN` memo on another, a full Solana transaction on another. Follow the matching guide:
+
+- **[Sending the deposit](/api-reference/deposit-actions/overview)** — per-network execution (EVM, Bitcoin,
+ Solana, Starknet, TON, Tron, Fuel).
+- **[Depository](/api-reference/depository)** — when you created the swap with `use_depository: true`.
+
+
+ Submit the returned `call_data` **verbatim**. It encodes the swap `id` and the assigned receiver;
+ altering it means Layerswap can't match your deposit to the swap.
+
+
+After broadcasting, you can optionally speed up matching by reporting the transaction hash to
+[`POST /api/v2/swaps/{swapId}/deposit_speedup`](/api-reference/swaps/speed-up-deposit).
+
+## 4. Track the transfer
+
+Poll the swap to follow it to completion:
+
+- [`GET /api/v2/swaps/{swapId}`](/api-reference/swaps/get-swap-details) — the swap and its `transactions`
+ (`input` = your source deposit, `output` = the destination delivery).
+- [`GET /api/v2/swaps/by_transaction_hash/{transactionHash}`](/api-reference/swaps/get-swap-by-transaction-hash)
+ — look up a swap by its source transaction hash.
+
+A typical swap moves `user_transfer_pending` → `ls_transfer_pending` → `completed`. Other terminal states
+are `failed`, `expired`, `pending_refund`, and `refunded`. See the [Swap Lifecycle](/api-reference/swap-lifecycle)
+for every state and transition, and [Refunds](/api-reference/refunds) for failure handling.
+
+## Discovering routes & tokens
+
+Most integrations already know the route they want and can go straight to a quote. When you need to
+enumerate what's available — to build a token picker, or to check support up front — three endpoints list
+it:
+
+| Endpoint | Returns |
+| --- | --- |
+| [`GET /api/v2/sources`](/api-reference/swaps/get-sources) | Source networks and their tokens (optionally filtered by destination) |
+| [`GET /api/v2/destinations`](/api-reference/swaps/get-destinations) | Destination networks and their tokens (optionally filtered by source) |
+| [`GET /api/v2/limits`](/api-reference/swaps/get-swap-route-limits) | Min / max amount for a specific route |
+
+Each token carries a stable `contract` and a `group` — its **token family** (e.g. `ETH`, `USDC`, `USDT`) —
+so you can resolve a token reliably even when its symbol changes:
+
+```js
+const sources = await fetch("https://api.layerswap.io/api/v2/sources", {
+ headers: { "X-LS-APIKEY": process.env.LAYERSWAP_API_KEY },
+}).then((r) => r.json());
+
+// Resolve the USDT-family token on Arbitrum by its `group`, not its symbol.
+const arbitrum = sources.data.find((n) => n.name === "ARBITRUM_MAINNET");
+const usdt = arbitrum.tokens.find((t) => t.group === "USDT");
+
+usdt.symbol; // "USDT0" → display label, may change
+usdt.contract; // "0x..." → stable identifier, use this as source_token
+```
+
+Or browse everything at [Networks & Tokens](/networks-tokens).
+
+## Next steps
+
+
+
+ Execute the deposit on each supported network.
+
+
+ Fund a swap by calling our on-chain contract.
+
+
+ Every swap state and how to react to it.
+
+
+ How quotes and fees are calculated.
+
+
diff --git a/lifi-integration.mdx b/lifi-integration.mdx
index 17ab30d..7d752ef 100644
--- a/lifi-integration.mdx
+++ b/lifi-integration.mdx
@@ -291,17 +291,21 @@ These can be discussed if needed for the integration.
For Bitcoin, the API returns a **deposit address** and a **numeric memo**.
- `to_address` — the Bitcoin deposit address to send funds to
-- `call_data` — a numeric memo string (e.g. `"12345"`) that **must** be embedded as an `OP_RETURN` output in the transaction. This is how Layerswap identifies and matches the deposit. Convert `call_data` to a hex string with `Number(call_data).toString(16)`, then embed that string in `OP_RETURN` as ASCII/UTF‑8 bytes — not as the raw bytes the hex represents. In `bitcoinjs-lib`:
+- `call_data` — the swap's numeric memo **followed by a `;` terminator** (e.g. `"12345;"`) that **must** be embedded as an `OP_RETURN` output in the transaction. This is how Layerswap identifies and matches the deposit. Convert the numeric part to hex (use `BigInt` — `Number` loses precision and fails on the `;`), keep the terminator as-is, and embed the resulting string in `OP_RETURN` as ASCII/UTF‑8 bytes — not as the raw bytes the hex represents. In `bitcoinjs-lib`:
```js
-const hexMemo = Number(call_data).toString(16);
-const data = Buffer.from(hexMemo, 'utf8'); // ← 'utf8', NOT 'hex'
+const raw = String(call_data); // e.g. "12345;"
+const semi = raw.indexOf(';');
+const seq = semi === -1 ? raw : raw.slice(0, semi); // "12345"
+const tail = semi === -1 ? '' : raw.slice(semi); // ";" plus anything after it
+const hexMemo = BigInt(seq).toString(16) + tail; // "3039;"
+const data = Buffer.from(hexMemo, 'utf8'); // ← 'utf8', NOT 'hex'
const opReturn = bitcoin.script.compile([bitcoin.opcodes.OP_RETURN, data]);
```
The `OP_RETURN` payload must be at most 80 bytes (standard relay limit).
-Layerswap matches the source transaction to the swap by reading the `OP_RETURN` output: it ASCII‑decodes the bytes, parses the resulting string as hex, and converts to a decimal that must equal `call_data`. If the `OP_RETURN` bytes are not valid ASCII hex characters, the deposit will not be matched and the swap will expire.
+Layerswap matches the source transaction to the swap by reading the `OP_RETURN` output: it ASCII‑decodes the bytes, takes the part **before** the `;` terminator, and parses it as hex — the result must equal the numeric memo. Anything after the `;` is ignored for matching, so you can append your own data there (within the 80-byte limit). If the part before the `;` isn't valid ASCII hex, the deposit will not be matched and the swap will expire.
- `amount` — the BTC amount to send (decimal)
- `amount_in_base_units` — the amount in satoshis
@@ -318,13 +322,13 @@ Example deposit action response:
"network": { "name": "BITCOIN_MAINNET", "type": "bitcoin", "..." : "..." },
"token": { "symbol": "BTC", "decimals": 8, "..." : "..." },
"fee_token": { "symbol": "BTC", "decimals": 8, "..." : "..." },
- "call_data": "11177265"
+ "call_data": "11177265;"
}
]
}
```
-Example input transaction: [`0ecfb9b7...9e1532`](https://mempool.space/tx/0ecfb9b7117b34fb7c1881a3615bbe5208881293956892f5af11b13bb89e1532). The `OP_RETURN` scriptpubkey is `6a06616138643331` — `6a` is `OP_RETURN`, `06` is the push length, and `616138643331` is the ASCII string `aa8d31`. That string is `Number("11177265").toString(16)` — i.e. `call_data = "11177265"`, hex string = `"aa8d31"`, embedded as the six ASCII bytes `61 61 38 64 33 31`.
+Example input transaction: [`0ecfb9b7...9e1532`](https://mempool.space/tx/0ecfb9b7117b34fb7c1881a3615bbe5208881293956892f5af11b13bb89e1532). The `OP_RETURN` scriptpubkey is `6a06616138643331` — `6a` is `OP_RETURN`, `06` is the push length, and `616138643331` is the ASCII string `aa8d31` — `BigInt("11177265").toString(16)`, embedded as the six ASCII bytes `61 61 38 64 33 31`. (This transaction predates the `;` terminator; with today's `call_data = "11177265;"` the payload would be the ASCII bytes of `aa8d31;` — both forms are matched.)
`gas_limit` and `encoded_args` are not applicable for Bitcoin.
diff --git a/snippets/depository-deposit-evm.mdx b/snippets/depository-deposit-evm.mdx
new file mode 100644
index 0000000..35195f4
--- /dev/null
+++ b/snippets/depository-deposit-evm.mdx
@@ -0,0 +1,25 @@
+```ts viem
+import { createWalletClient, custom, parseAbi } from "viem";
+
+// `swap` is the POST /api/v2/swaps response
+const action = swap.deposit_actions[0];
+const wallet = createWalletClient({ transport: custom(window.ethereum) });
+
+// ERC-20 only: approve the Depository (to_address) to pull the tokens.
+// The amount is the 4th depositERC20 argument, returned in encoded_args.
+if (action.token.contract) {
+ await wallet.writeContract({
+ address: action.token.contract,
+ abi: parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]),
+ functionName: "approve",
+ args: [action.to_address, BigInt(action.encoded_args[3])],
+ });
+}
+
+// Submit the prepared depositNative / depositERC20 call exactly as returned
+await wallet.sendTransaction({
+ to: action.to_address, // the Depository contract
+ data: action.call_data,
+ value: BigInt(action.amount_in_base_units), // 0 for ERC-20, the deposit amount for native
+});
+```
diff --git a/snippets/depository-deposit-tron.mdx b/snippets/depository-deposit-tron.mdx
new file mode 100644
index 0000000..fc583b3
--- /dev/null
+++ b/snippets/depository-deposit-tron.mdx
@@ -0,0 +1,51 @@
+```ts TronWeb
+import { TronWeb } from "tronweb";
+
+// `swap` is the POST /api/v2/swaps response; PRIVATE_KEY signs the deposit
+const action = swap.deposit_actions[0];
+const [id, , receiverHex, amountHex] = action.encoded_args; // [id, token, receiver, amount]
+const amount = BigInt(amountHex).toString();
+
+const tronWeb = new TronWeb({
+ fullNode: action.network.node_url,
+ solidityNode: action.network.node_url,
+ privateKey: PRIVATE_KEY,
+});
+const sender = tronWeb.defaultAddress.base58;
+
+// encoded_args carry EVM-style hex addresses — convert the receiver to a Tron address
+const receiver = tronWeb.address.fromHex("41" + receiverHex.replace(/^0x/, ""));
+
+// 1) Approve the Depository (to_address) to spend the TRC-20 amount
+const approveTx = (
+ await tronWeb.transactionBuilder.triggerSmartContract(
+ action.token.contract, // TRC-20 token (base58)
+ "approve(address,uint256)",
+ { feeLimit: 100_000_000 },
+ [
+ { type: "address", value: action.to_address },
+ { type: "uint256", value: amount },
+ ],
+ sender,
+ )
+).transaction;
+await tronWeb.trx.sendRawTransaction(await tronWeb.trx.sign(approveTx));
+
+// 2) Call depositERC20(bytes32 id, address token, address receiver, uint256 amount)
+// on the Depository (wait for the approval to confirm first)
+const depositTx = (
+ await tronWeb.transactionBuilder.triggerSmartContract(
+ action.to_address, // Depository contract (base58)
+ "depositERC20(bytes32,address,address,uint256)",
+ { feeLimit: 100_000_000 },
+ [
+ { type: "bytes32", value: id },
+ { type: "address", value: action.token.contract },
+ { type: "address", value: receiver },
+ { type: "uint256", value: amount },
+ ],
+ sender,
+ )
+).transaction;
+await tronWeb.trx.sendRawTransaction(await tronWeb.trx.sign(depositTx));
+```