> ## Documentation Index
> Fetch the complete documentation index at: https://docs.houdiniswap.com/llms.txt
> Use this file to discover all available pages before exploring further.

# DEX Swap Integration

> Integrate on-chain DEX swaps using the unified v2 quotes and exchanges API

## Overview

DEX swaps execute on-chain through smart contracts. Unlike CEX swaps, DEX swaps require users to connect their wallet, sign transactions, and broadcast them to the blockchain. In API v2, DEX swaps use the same `/quotes` and `/exchanges` endpoints as CEX swaps — pass `types=dex` to get only DEX quotes, or filter the response by `type: "dex"`. The `quoteId` is the single identifier passed through the entire flow — no route objects required.

<Info>
  **Best For**: Users who want true decentralized swaps, keep custody of their funds, and interact directly with on-chain liquidity sources like Uniswap, CowSwap, and 1inch.
</Info>

<Note>
  Looking for the v1 DEX swap guide? See [API v1 — DEX Swap](/api-v1/swap-flows/dex-swap).
</Note>

<Note>
  Fixed rate is not available for DEX swaps. For guaranteed output amounts, use a [Fixed Rate Standard Swap](/developer-hub/swap-flows/fixed-rate-swap) (CEX-based).
</Note>

### Supported Networks

* **EVM** (Ethereum, BSC, Polygon, etc.)
* **Solana**
* **SUI**
* **TRON**
* **TON**
* **Stellar**

<Warning>
  **Stellar Trustline Requirement**: Ensure the destination account has required [trustlines](https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/accounts#trustlines) before initiating the swap. Houdini does not create trustlines automatically.
</Warning>

## Key Differences from CEX Swaps

| Feature       | DEX                          | CEX (Standard/Private)        |
| ------------- | ---------------------------- | ----------------------------- |
| **Execution** | On-chain via smart contracts | Off-chain via exchanges       |
| **Wallet**    | Required                     | Not required                  |
| **Custody**   | User retains custody         | User sends to deposit address |
| **Approvals** | May require token approvals  | Not required                  |
| **Speed**     | Seconds to minutes           | 3–45 minutes                  |

## How It Works

<Steps>
  <Step title="Get Tokens">
    Bulk fetch DEX-supported tokens and cache to your DB, or search by name/symbol. Note each token's `id`.
  </Step>

  <Step title="Get Quotes">
    Call `GET /quotes` with token IDs and optional slippage. Select the quote with `type: "dex"`.
  </Step>

  <Step title="Check Approvals (if requiresApproval: true)">
    If the quote has `requiresApproval: true`, call `POST /dex/approve` with `quoteId` and `addressFrom`. Returns on-chain approval transactions and/or signatures needed. Skip this step if `requiresApproval: false`.
  </Step>

  <Step title="Handle Approvals (if needed)">
    Broadcast approval transactions via user's wallet.
  </Step>

  <Step title="Handle Signatures (if needed)">
    Have user sign EIP-712 typed data. For chained signatures, call `POST /dex/chainSignatures` until `isComplete: true`.
  </Step>

  <Step title="Create Order">
    Call `POST /exchanges` with `quoteId`, `addressTo`, `addressFrom`, and any collected signatures.
  </Step>

  <Step title="Broadcast & Confirm">
    If not off-chain, have user broadcast the transaction. Then call `POST /dex/confirmTx` with the transaction hash.
  </Step>

  <Step title="Monitor Status">
    Poll `GET /orders/{houdiniId}` until `statusLabel` is `FINISHED`.
  </Step>
</Steps>

## Integration Guide

### Step 1: Get Tokens

There are two approaches for getting tokens. Choose the one that fits your integration:

<Tabs>
  <Tab title="Bulk Fetch + Cache">
    Fetch all DEX-supported tokens once and store them in your backend database.

    <Warning>
      Cache the token list in your backend database. Never call `/tokens` on every user request — refresh every 24 hours.
    </Warning>

    <CodeGroup>
      ```javascript JavaScript theme={null}
      async function fetchAllDexTokens() {
        let page = 1;
        let allTokens = [];

        while (true) {
          const response = await fetch(
            `https://api-partner.houdiniswap.com/v2/tokens?hasDex=true&pageSize=20&page=${page}`,
            {
              headers: { 'Authorization': `${API_KEY}:${API_SECRET}` }
            }
          );

          const { tokens, totalPages } = await response.json();
          allTokens = allTokens.concat(tokens);

          if (page >= totalPages) break;
          page++;
        }

        return allTokens; // save to your DB
      }
      ```

      ```bash cURL theme={null}
      curl -X GET "https://api-partner.houdiniswap.com/v2/tokens?hasDex=true&pageSize=20&page=1" \
        -H "Authorization: your_api_key:your_api_secret"
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Token Search">
    Search for specific tokens by name or symbol on demand.

    <CodeGroup>
      ```javascript JavaScript theme={null}
      async function searchTokens(query) {
        const params = new URLSearchParams({
          term: query,
          hasDex: 'true',
          pageSize: '20',
          page: '1'
        });

        const response = await fetch(
          `https://api-partner.houdiniswap.com/v2/tokens?${params}`,
          {
            headers: { 'Authorization': `${API_KEY}:${API_SECRET}` }
          }
        );

        const { tokens } = await response.json();
        return tokens; // use token `id` in /quotes requests
      }
      ```

      ```bash cURL theme={null}
      curl -X GET "https://api-partner.houdiniswap.com/v2/tokens?term=ethereum&hasDex=true&pageSize=20&page=1" \
        -H "Authorization: your_api_key:your_api_secret"
      ```
    </CodeGroup>
  </Tab>
</Tabs>

### Step 2: Get DEX Quote

Call `GET /quotes` with token IDs and optional `slippage`. Select the quote with `type: "dex"`.

<CodeGroup>
  ```javascript JavaScript theme={null}
  const params = new URLSearchParams({
    amount: '1',
    from: '6689b73ec90e45f3b3e51566',  // ETH token id from /tokens
    to:   '6689b73ec90e45f3b3e51553',  // USDT token id from /tokens
    types: 'dex',                      // only return DEX quotes
    slippage: '0.5',                   // 0.5% slippage (optional)
  });

  const response = await fetch(
    `https://api-partner.houdiniswap.com/v2/quotes?${params}`,
    {
      headers: {
        'Authorization': `${API_KEY}:${API_SECRET}`,
        'x-user-ip': userIp,
        'x-user-agent': userAgent,
        'x-user-timezone': userTimezone
      }
    }
  );

  const { quotes } = await response.json();

  // Select a DEX quote
  const dexQuote = quotes.find(q => q.type === 'dex');
  console.log('DEX provider:', dexQuote.swapName);      // e.g., "Bungee"
  console.log('Amount out:', dexQuote.amountOut);
  console.log('Quote ID:', dexQuote.quoteId);            // needed for next steps
  console.log('Requires approval:', dexQuote.requiresApproval);
  ```

  ```bash cURL theme={null}
  curl -X GET "https://api-partner.houdiniswap.com/v2/quotes?amount=1&from=6689b73ec90e45f3b3e51566&to=6689b73ec90e45f3b3e51553&types=dex&slippage=0.5" \
    -H "Authorization: your_api_key:your_api_secret" \
    -H "x-user-ip: 192.168.1.1" \
    -H "x-user-agent: Mozilla/5.0..." \
    -H "x-user-timezone: America/New_York"
  ```
</CodeGroup>

### Quotes Response (DEX quote)

```json theme={null}
{
  "quotes": [
    {
      "quoteId": "69af9eb7f9c5affabcacccba",
      "type": "dex",
      "swap": "bg",
      "swapName": "Bungee",
      "logoUrl": "https://api.houdiniswap.com/assets/logos/bungee-6ohrmd.png",
      "amountOut": 0.007372135451768274,
      "amountOutUsd": 15.019251555,
      "netAmountOut": 0.007372135451768274,
      "duration": 1,
      "markupSupported": true,
      "apiMarkupValue": 10,
      "markupType": "bp",
      "restrictedCountries": [],
      "rewardsAvailable": false,
      "requiresApproval": true
    }
  ],
  "total": 3
}
```

**Key Fields:**

* `quoteId`: Pass to `/dex/approve` and `/exchanges`
* `type`: `"dex"` for on-chain swaps
* `requiresApproval`: If `true`, call the approve/allowance flow (Step 3). If `false`, skip directly to Step 6.
* `markupSupported`: Whether a fee markup can be applied to this route
* `apiMarkupValue` / `markupType`: Markup amount and type (`"bp"` = basis points)
* `restrictedCountries`: List of country codes where this route is unavailable

### Step 3: Check Approvals and Signatures

<Note>
  Only run this step if `requiresApproval: true` on the selected quote. If `requiresApproval` is `false`, skip Steps 3–5 and go directly to [Step 6: Create Order](#step-6-create-order).
</Note>

Call `POST /dex/approve` with just the `quoteId` and user's wallet address:

<CodeGroup>
  ```javascript JavaScript theme={null}
  const response = await fetch('https://api-partner.houdiniswap.com/v2/dex/approve', {
    method: 'POST',
    headers: {
      'Authorization': `${API_KEY}:${API_SECRET}`,
      'Content-Type': 'application/json',
      'x-user-ip': userIp,
      'x-user-agent': userAgent,
      'x-user-timezone': userTimezone
    },
    body: JSON.stringify({
      quoteId: '69155e0bdb5ab0cbe27e2709',           // from /quotes
      addressFrom: '0x45CF73349a4895fabA18c0f51f06D79f0794898D'  // user wallet
    })
  });

  const { approvals, signatures } = await response.json();
  // approvals: on-chain approval transactions to broadcast (may be empty)
  // signatures: EIP-712 typed data to sign (may be empty)
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api-partner.houdiniswap.com/v2/dex/approve" \
    -H "Authorization: your_api_key:your_api_secret" \
    -H "Content-Type: application/json" \
    -H "x-user-ip: 192.168.1.1" \
    -H "x-user-agent: Mozilla/5.0..." \
    -H "x-user-timezone: America/New_York" \
    -d '{
      "quoteId": "69155e0bdb5ab0cbe27e2709",
      "addressFrom": "0x45CF73349a4895fabA18c0f51f06D79f0794898D"
    }'
  ```
</CodeGroup>

**Response Fields:**

* `approvals`: Array of `{ data, to, from, fromChain }` transactions to broadcast (may be empty)
* `signatures`: Array of EIP-712 typed data objects to sign (may be empty)

<Note>
  You may receive both `approvals` and `signatures`. Handle approvals first, then signatures.
</Note>

#### Signature Types

* **SINGLE**: User signs once — collect the result and proceed
* **CHAINED** (e.g., CowSwap): User signs → call `/dex/chainSignatures` → user signs again → repeat until `isComplete: true`

### Step 4: Send Approval Transactions (if needed)

<CodeGroup>
  ```javascript JavaScript theme={null}
  if (approvals && approvals.length > 0) {
    for (const approval of approvals) {
      const tx = await walletProvider.sendTransaction({
        to: approval.to,
        data: approval.data,
        from: approval.from
      });
      await tx.wait();
      console.log('Approval confirmed:', tx.hash);
    }
  }
  ```
</CodeGroup>

<Warning>
  Skip this step if `approvals` is empty.
</Warning>

### Step 5: Process Signatures (if needed)

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function processSignatures(signatures, quoteId, addressFrom) {
    if (!signatures || signatures.length === 0) return [];

    const results = [];

    for (const sig of signatures) {
      const signatureResult = await walletProvider.signTypedData({
        domain: sig.data.domain,
        types: sig.data.types,
        primaryType: sig.data.primaryType,
        message: sig.data.message
      });

      const signatureObject = {
        signature: signatureResult,
        key: sig.key,
        swapRequiredMetadata: sig.swapRequiredMetadata
      };

      if (sig.type === 'CHAINED' && !sig.isComplete) {
        // Get next signature in the chain
        const chainResponse = await fetch('https://api-partner.houdiniswap.com/v2/dex/chainSignatures', {
          method: 'POST',
          headers: {
            'Authorization': `${API_KEY}:${API_SECRET}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            quoteId,
            addressFrom,
            previousSignature: signatureObject,
            signatureKey: sig.key,
            signatureStep: sig.step
          })
        });

        const nextSignatures = await chainResponse.json();
        const chainResults = await processSignatures(nextSignatures, quoteId, addressFrom);
        if (chainResults.length > 0) {
          results.push(chainResults[chainResults.length - 1]); // only final
        }
      } else {
        results.push(signatureObject);
      }
    }

    return results;
  }

  const collectedSignatures = await processSignatures(signatures, dexQuote.quoteId, userAddress);
  ```
</CodeGroup>

**Key points:**

* **SINGLE**: Collect one signature and move on
* **CHAINED**: Sign → `/dex/chainSignatures` → sign again → repeat until `isComplete: true`. Only the final result is passed to `/exchanges`

<Warning>
  Skip this step if `signatures` is empty.
</Warning>

### Step 6: Create Order

Call `POST /exchanges` with the `quoteId`, addresses, and any collected signatures:

<CodeGroup>
  ```javascript JavaScript theme={null}
  const response = await fetch('https://api-partner.houdiniswap.com/v2/exchanges', {
    method: 'POST',
    headers: {
      'Authorization': `${API_KEY}:${API_SECRET}`,
      'Content-Type': 'application/json',
      'x-user-ip': userIp,
      'x-user-agent': userAgent,
      'x-user-timezone': userTimezone
    },
    body: JSON.stringify({
      quoteId: '69155e0bdb5ab0cbe27e2709',
      addressTo: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
      addressFrom: '0x45CF73349a4895fabA18c0f51f06D79f0794898D',
      signatures: collectedSignatures  // empty array if none required
    })
  });

  const order = await response.json();
  console.log('Order ID:', order.houdiniId);
  console.log('Off-chain?', order.metadata?.offChain);
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api-partner.houdiniswap.com/v2/exchanges" \
    -H "Authorization: your_api_key:your_api_secret" \
    -H "Content-Type: application/json" \
    -H "x-user-ip: 192.168.1.1" \
    -H "x-user-agent: Mozilla/5.0..." \
    -H "x-user-timezone: America/New_York" \
    -d '{
      "quoteId": "69155e0bdb5ab0cbe27e2709",
      "addressTo": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
      "addressFrom": "0x45CF73349a4895fabA18c0f51f06D79f0794898D",
      "signatures": []
    }'
  ```
</CodeGroup>

**Request Fields:**

* `quoteId` (required): From `/quotes`
* `addressTo` (required): Destination wallet address
* `addressFrom` (optional): Source wallet address — required for DEX swaps
* `signatures` (optional): Collected from Step 5
* `destinationTag` (optional): Memo for chains that require it

**Order Response contains:**

* `houdiniId`: Use for status polling
* `metadata.offChain`: `true` if Houdini backend broadcasts (e.g., CowSwap)
* `metadata.to`: DEX router address (when `offChain: false`)
* `metadata.data`: Encoded swap calldata (when `offChain: false`)
* `metadata.value`: ETH value for native swaps (when `offChain: false`)

### Step 7: Broadcast Transaction and Confirm

<Tabs>
  <Tab title="On-Chain (offChain: false)">
    User broadcasts the transaction, then confirm with Houdini:

    ```javascript theme={null}
    if (!order.metadata?.offChain) {
      const tx = await walletProvider.sendTransaction({
        to: order.metadata.to,
        data: order.metadata.data,
        value: order.metadata.value || '0'
      });

      const receipt = await tx.wait();

      await fetch('https://api-partner.houdiniswap.com/v2/dex/confirmTx', {
        method: 'POST',
        headers: {
          'Authorization': `${API_KEY}:${API_SECRET}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          id: order.houdiniId,
          txHash: receipt.hash
        })
      });
    }
    ```
  </Tab>

  <Tab title="Off-Chain (offChain: true)">
    Houdini backend handles execution — still call confirmTx to start processing:

    ```javascript theme={null}
    if (order.metadata?.offChain) {
      await fetch('https://api-partner.houdiniswap.com/v2/dex/confirmTx', {
        method: 'POST',
        headers: {
          'Authorization': `${API_KEY}:${API_SECRET}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          id: order.houdiniId
        })
      });
    }
    ```
  </Tab>
</Tabs>

<Warning>
  You must call `/dex/confirmTx` in both cases. For on-chain swaps, pass the `txHash`. For off-chain swaps, omit `txHash`. Without this call, the order will not be processed.
</Warning>

### Step 8: Monitor Order Status

Poll `GET /orders/{houdiniId}` to track progress, or subscribe via the [WebSocket API](/developer-hub/core-concepts/websocket-order-updates) for real-time updates:

<CodeGroup>
  ```javascript JavaScript theme={null}
  const response = await fetch(
    `https://api-partner.houdiniswap.com/v2/orders/${order.houdiniId}`,
    {
      headers: {
        'Authorization': `${API_KEY}:${API_SECRET}`,
        'x-user-ip': userIp,
        'x-user-agent': userAgent,
        'x-user-timezone': userTimezone
      }
    }
  );

  const status = await response.json();
  console.log('Status:', status.statusLabel);
  // WAITING   — awaiting transaction
  // CONFIRMING — transaction submitted, waiting for confirmations
  // EXCHANGING — swap processing
  // FINISHED   — swap complete
  // FAILED     — swap failed
  ```

  ```bash cURL theme={null}
  curl -X GET "https://api-partner.houdiniswap.com/v2/orders/iBQMRX3xvXrFMGQi71ogo9" \
    -H "Authorization: your_api_key:your_api_secret" \
    -H "x-user-ip: 192.168.1.1" \
    -H "x-user-agent: Mozilla/5.0..." \
    -H "x-user-timezone: America/New_York"
  ```
</CodeGroup>

<Tip>
  Poll every 30 seconds. DEX swaps typically complete in seconds to a few minutes depending on network congestion. For real-time updates without polling, use the [WebSocket API](/developer-hub/core-concepts/websocket-order-updates).
</Tip>

## Example Repositories

See full working integrations on GitHub:

<CardGroup cols={2}>
  <Card title="Next.js Example" icon="react" href="https://github.com/HoudiniSwap/houdini-api-examples/tree/main/nextjs-example">
    Full Next.js integration showing standard, private, and DEX swap flows
  </Card>

  <Card title="Node.js Example" icon="node-js" href="https://github.com/HoudiniSwap/houdini-api-examples/tree/main/node-examples">
    Backend Node.js integration with token fetching, quoting, and order tracking
  </Card>
</CardGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Standard Swaps" icon="bolt" href="/developer-hub/swap-flows/standard-swap">
    Fast single-hop CEX swaps
  </Card>

  <Card title="Private Swaps" icon="lock" href="/developer-hub/swap-flows/private-swap">
    Multi-hop privacy swaps
  </Card>

  <Card title="Order Lifecycle" icon="rotate" href="/developer-hub/core-concepts/order-lifecycle">
    Understand all order statuses
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/developer-hub/troubleshooting/errors">
    Handle errors and edge cases
  </Card>
</CardGroup>
