# Analytics & Settlement (Partner Dashboard) Source: https://docs.houdiniswap.com/analytics-and-settlement Understand how commissions are calculated, tracked, and withdrawn. ## Overview The [Partner Dashboard](https://app.houdiniswap.com/partner/login) displays: * All-time volume * All-time swap count * Commission balance * Withdrawable balance * Commission rate * Current tier (Free / Pro) All earnings and settlement operations are managed through the Partner Portal. ## Commission ### **Rate** Your commission rate is displayed under **Commission Details**. The rate defines your share of eligible swap volume. Commission terms are defined per partner agreement. ### Commission Balance **Commission Balance** represents: * Total accumulated commission * Across all completed eligible swaps * Before withdrawal The balance updates automatically as swaps settle. ## Withdrawable Balance **Withdrawable Balance** represents: * The portion of commission available for withdrawal Withdrawable balance may differ from total commission balance depending on settlement state. ## Volume & Swap Metrics The dashboard displays: * **All-time volume**: total swap volume processed via your API key * **All-time swaps**: total number of completed swaps * **Daily and monthly charts**: swap count and volume over time Metrics are visible directly in the dashboard. ## Commission Wallet To withdraw earnings: 1. Add or connect an EVM wallet under **Commission Wallet** 2. Ensure the wallet is correctly linked to your account Withdrawals require a linked wallet. ## Withdrawals To withdraw earned commission: 1. Navigate to the Withdraw section 2. Enter the desired amount 3. Confirm the withdrawal Gas fees are covered by the withdrawing wallet. Processing time may take several minutes depending on network conditions. ## Tier Visibility Your current tier (Free or Pro) is visible in the dashboard. Tier determines: * Default rate limits * Commission structure Tier upgrades are handled manually. # Get available chains Source: https://docs.houdiniswap.com/api-reference/chains/get-available-chains https://api-partner.houdiniswap.com/v2/openapi.json get /chains Get the list of available `enabled` blockchain networks/chains for both CEX and DEX swaps. Use the `shortName` field as the chain parameter in endpoints. # Get partner commission summary Source: https://docs.houdiniswap.com/api-reference/commissions/get-partner-commission-summary https://api-partner.houdiniswap.com/v2/openapi.json get /commissions/summary # Get partner commissions Source: https://docs.houdiniswap.com/api-reference/commissions/get-partner-commissions https://api-partner.houdiniswap.com/v2/openapi.json get /commissions # Check token allowance Source: https://docs.houdiniswap.com/api-reference/dex/check-token-allowance https://api-partner.houdiniswap.com/v2/openapi.json post /dex/allowance Check if token allowance is sufficient for a DEX swap Returns true if the current allowance is enough to proceed with the swap, or false if an approval transaction is needed first. # Confirm DEX transaction Source: https://docs.houdiniswap.com/api-reference/dex/confirm-dex-transaction https://api-partner.houdiniswap.com/v2/openapi.json post /dex/confirmTx Confirms a DEX transaction with the provided transaction hash. This endpoint should be called after the user has signed and submitted the transaction. Without it the Order status may not be determined # Get next chain signature Source: https://docs.houdiniswap.com/api-reference/dex/get-next-chain-signature https://api-partner.houdiniswap.com/v2/openapi.json post /dex/chainSignatures Get the next signature requirement in a multi-step signature chain Used for swaps that require multiple sequential signatures (e.g. permit + bridge). Call this endpoint recursively: sign the returned data with the wallet, then pass the signature back until `isComplete` is true. Only needed when a signature returned by the approve endpoint has type "CHAINED". # Get token approval data Source: https://docs.houdiniswap.com/api-reference/dex/get-token-approval-data https://api-partner.houdiniswap.com/v2/openapi.json post /dex/approve Get approval transaction data for a DEX swap Returns a list of required token approval transactions for the specified route. This endpoint should be polled until an empty approvals list is returned, indicating that all necessary approvals have been granted. Returns either approval transactions or permit signatures depending on token and swap provider support. If the token supports EIP-2612 permits and the swap provider is configured for it, signatures will be returned # Build multi exchange batch transaction data Source: https://docs.houdiniswap.com/api-reference/exchanges/build-multi-exchange-batch-transaction-data https://api-partner.houdiniswap.com/v2/openapi.json post /exchanges/multi/{multiId}/tx/build Builds batched transaction data for all orders in a multi exchange group. Supports Solana (up to 10 deposits per batch) and EVM ERC-4337 (up to 20 legs per batch). All orders must share the same source token chain. For EVM the built UserOp is stored server-side until submitted, hence POST. # Create exchange Source: https://docs.houdiniswap.com/api-reference/exchanges/create-exchange https://api-partner.houdiniswap.com/v2/openapi.json post /exchanges Create a new exchange (swap). Use `type: "private"` or `type: "standard"` for centralized exchanges or `type: "dex"` for decentralized exchanges. # Create multi exchange Source: https://docs.houdiniswap.com/api-reference/exchanges/create-multi-exchange https://api-partner.houdiniswap.com/v2/openapi.json post /exchanges/multi Create a group of exchanges linked by a shared multiId. Each order is stored with status NEW and initialized asynchronously. Quotes are resolved internally — no quoteId required. # Get multi exchange batch transaction data (Solana) Source: https://docs.houdiniswap.com/api-reference/exchanges/get-multi-exchange-batch-transaction-data-solana https://api-partner.houdiniswap.com/v2/openapi.json get /exchanges/multi/{multiId}/tx Returns batched deposit-transaction data for a Solana multi exchange group. All orders must share the same source token chain; batches contain up to 10 deposit addresses each. This is read-only (no server-side side effect), so it is a GET. EVM multiswaps build a stored UserOp instead — use POST multi/{multiId}/tx/build for those. # Get multi exchange status Source: https://docs.houdiniswap.com/api-reference/exchanges/get-multi-exchange-status https://api-partner.houdiniswap.com/v2/openapi.json get /exchanges/multi/{multiId} Returns the status of all orders belonging to a multi exchange group. # Recover stuck assets from a failed EVM multiswap Source: https://docs.houdiniswap.com/api-reference/exchanges/recover-stuck-assets-from-a-failed-evm-multiswap https://api-partner.houdiniswap.com/v2/openapi.json post /exchanges/multi/recovery Find failed EVM multiswap batches for a wallet and return pre-built withdrawal UserOps. Builds and stores a recovery record, hence POST. Returns null (204) if no stuck assets are found. # Retry failed orders in a multiswap Source: https://docs.houdiniswap.com/api-reference/exchanges/retry-failed-orders-in-a-multiswap https://api-partner.houdiniswap.com/v2/openapi.json post /exchanges/multi/{multiId}/retry Retry a failed multiswap bundle. Only possible while the orders are within 30 minutes of creation (deposit addresses still valid) — the existing UserOp is reset to PENDING and `txData` (same shape as POST multi/{multiId}/tx/build) is returned so the frontend can re-sign and submit immediately. After 30 minutes, or for REFUNDED orders, this returns 400 — use POST /multi/recovery to withdraw the stuck funds instead. # Submit signed EVM UserOperation(s) Source: https://docs.houdiniswap.com/api-reference/exchanges/submit-signed-evm-useroperations https://api-partner.houdiniswap.com/v2/openapi.json post /exchanges/multi/{multiId}/tx Submit signed EVM UserOperation(s) to the bundler. Pass one signature per batch in the same order as returned by POST multi/{multiId}/tx/build. Most multi-swaps produce a single batch, so typically only one signature is needed. # Get all available swaps/exchanges Source: https://docs.houdiniswap.com/api-reference/liquidity-providers/get-all-available-swapsexchanges https://api-partner.houdiniswap.com/v2/openapi.json get /swaps Returns all available swap providers (exchanges) that can be used for trading. This includes both centralized exchanges (CEX) and decentralized exchanges (DEX). # Get DEX and CEX min/max amounts Source: https://docs.houdiniswap.com/api-reference/minmax/get-dex-and-cex-minmax-amounts https://api-partner.houdiniswap.com/v2/openapi.json get /minMax Get combined CEX and DEX min/max swap amounts for a token pair and return exchange limits. Supports both ObjectId and CEX token ID formats for tokenIdFrom/tokenIdTo: - ObjectId format: "6689b73ec90e45f3b3e51566" (for DEX operations) - CEX token ID format: "ETH", "BNB", "USDT" (for CEX operations) # Get all active orders Source: https://docs.houdiniswap.com/api-reference/orders/get-all-active-orders https://api-partner.houdiniswap.com/v2/openapi.json get /orders Returns a paginated list of orders created in the last 48 hours. # Get order details Source: https://docs.houdiniswap.com/api-reference/orders/get-order-details https://api-partner.houdiniswap.com/v2/openapi.json get /orders/{houdiniId} Returns order details for a given Houdini order ID created in the last 48 hours. # Get authenticated partner profile Source: https://docs.houdiniswap.com/api-reference/partner-profile/get-authenticated-partner-profile https://api-partner.houdiniswap.com/v2/openapi.json get /me # Beta - Multi Quotes Source: https://docs.houdiniswap.com/api-reference/quotes/beta--multi-quotes https://api-partner.houdiniswap.com/v2/openapi.json post /quotes/multi Returns a candidate array per order, mirroring the candidate resolution the multi-exchange create flow runs — CEX STANDARD + private two-leg, no DEX. # Get available quotes for a given amount and token pair. Source: https://docs.houdiniswap.com/api-reference/quotes/get-available-quotes-for-a-given-amount-and-token-pair https://api-partner.houdiniswap.com/v2/openapi.json get /quotes # Get Quotes By Chain And Address Source: https://docs.houdiniswap.com/api-reference/quotes/get-quotes-by-chain-and-address https://api-partner.houdiniswap.com/v2/openapi.json get /quotes/byChainAddress Get CEX/DEX quotes using token chain/address pairs. Resolves both tokens via `dex.getToken` and runs the same quote flow as v2 quote endpoint. # Get partner rate limits Source: https://docs.houdiniswap.com/api-reference/rate-limits/get-partner-rate-limits https://api-partner.houdiniswap.com/v2/openapi.json get /rateLimits # Get partner chart stats Source: https://docs.houdiniswap.com/api-reference/stats/get-partner-chart-stats https://api-partner.houdiniswap.com/v2/openapi.json get /stats/chart # Get partner volume stats Source: https://docs.houdiniswap.com/api-reference/stats/get-partner-volume-stats https://api-partner.houdiniswap.com/v2/openapi.json get /stats/volume # Get partner weekly volume stats Source: https://docs.houdiniswap.com/api-reference/stats/get-partner-weekly-volume-stats https://api-partner.houdiniswap.com/v2/openapi.json get /stats/weeklyVolume # Get system health Source: https://docs.houdiniswap.com/api-reference/system/get-system-health https://api-partner.houdiniswap.com/v2/openapi.json get /status # Get a token by its ID Source: https://docs.houdiniswap.com/api-reference/tokens/get-a-token-by-its-id https://api-partner.houdiniswap.com/v2/openapi.json get /tokens/{id} # Search tokens Source: https://docs.houdiniswap.com/api-reference/tokens/search-tokens https://api-partner.houdiniswap.com/v2/openapi.json get /tokens # Get partner withdrawals Source: https://docs.houdiniswap.com/api-reference/withdrawals/get-partner-withdrawals https://api-partner.houdiniswap.com/v2/openapi.json get /withdrawals # Get CEX Quote Source: https://docs.houdiniswap.com/api-v1/get-cex-quote Get a price quote for a CEX swap Deprecation of the REST v1 swap endpoints begins on September 30, 2026. Please migrate to the Partner API v2. Dashboard and authentication endpoints remain on v1 GraphQL and are not affected. [Read the migration guide](https://docs.houdiniswap.com/migration/v1-to-v2) # Get CEX Quote ## Endpoint ```text theme={null} GET /quote ``` Returns exchange rate details for a CEX swap. ## Query Parameters | Field | Type | Required | Description | | --------------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `amount` | string | Yes | The quantity the client transfers (e.g., `2`) | | `from` | string | Yes | TokenID of the source currency (e.g., `ETH`) | | `to` | string | Yes | TokenID of the destination currency (e.g., `BNB`) | | `anonymous` | boolean | Yes | Whether to route through XMR for privacy | | `useXmr` | boolean | No | Whether XMR specifically powers the anonymous transaction | | `rotatePayoutWallets` | boolean | No | Enable payout wallet rotation for privacy. Deprioritizes recently-used provider paths so consecutive swaps route through different wallets. See [Payout Wallet Rotation](/developer-hub/core-concepts/payout-wallet-rotation) | | `deviationThreshold` | number | No | Maximum price deviation percentage allowed when rotating (default: `5`). If the rotated quote deviates more than this from the best quote, rotation is skipped. Only used when `rotatePayoutWallets` is `true` | | `rotationLookback` | number | No | Number of recent orders to check for path deduplication (default: `10`). Only used when `rotatePayoutWallets` is `true` | ## Example Request ```text theme={null} GET /quote?amount=2&from=ETH&to=BNB&anonymous=false&useXmr=false&rotatePayoutWallets=true&deviationThreshold=5&rotationLookback=10 ``` ## Example Response ```json theme={null} { "amountIn": 2, "amountOut": 12345, "min": 0.1, "max": 99999999, "useXmr": false, "duration": 15 } ``` ## Response Fields | Field | Type | Description | | ------------ | ------- | ----------------------------------------- | | `amountIn` | number | The input amount quoted | | `amountOut` | number | The output amount quoted | | `min` | number | Minimum exchange threshold | | `max` | number | Maximum exchange threshold | | `useXmr` | boolean | Whether XMR is used for anonymous routing | | `duration` | number | Estimated exchange timeframe in minutes | | `deviceInfo` | string | *(Optional)* Device info for tracking | | `isMobile` | boolean | *(Optional)* Whether client is mobile | | `clientId` | string | *(Optional)* Client identifier | # Get CEX Tokens Source: https://docs.houdiniswap.com/api-v1/get-cex-tokens Retrieve supported CEX token list Deprecation of the REST v1 swap endpoints begins on September 30, 2026. Please migrate to the Partner API v2. Dashboard and authentication endpoints remain on v1 GraphQL and are not affected. [Read the migration guide](https://docs.houdiniswap.com/migration/v1-to-v2) # Get CEX Tokens ## Endpoint ```text theme={null} GET /tokens ``` Returns a list of supported tokens for centralized exchange (CEX) swaps. ## Parameters None required. ## Example Request ```text theme={null} GET /tokens ``` ## Example Response ```json theme={null} [ { "id": "USDT", "name": "Tether", "symbol": "USDT", "network": { "name": "Ethereum Mainnet", "shortName": "ETH", "addressValidation": "^(0x)[0-9A-Fa-f]{40}$", "memoNeeded": false, "explorerUrl": "https://etherscan.io/tx/", "addressUrl": "https://etherscan.io/address/", "priority": 1, "kind": "evm", "chainId": 1 }, "color": "#26a17b", "keyword": "usdt usdterc20 tether usdteth erc-20 erc20 eth", "displayName": "USDT (ETHEREUM)", "chain": 1, "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", "hasMarkup": true, "networkPriority": 2, "icon": "https://...", "hasFixed": true, "hasFixedReverse": true } ] ``` ## Response Fields (TokenDTO) | Field | Type | Description | | ----------------- | ------- | ------------------------------------------ | | `id` | string | Unique token identifier | | `name` | string | Full token name (e.g., Tether) | | `symbol` | string | Ticker symbol (e.g., USDT) | | `network` | object | Associated blockchain network (NetworkDTO) | | `color` | string | Hex color code *(deprecated)* | | `keyword` | string | Searchability keywords | | `displayName` | string | User-friendly display name | | `icon` | string | Token icon URL | | `hasFixed` | boolean | Fixed swap support *(deprecated)* | | `hasFixedReverse` | boolean | Reverse fixed swap support *(deprecated)* | ## Network Fields (NetworkDTO) | Field | Type | Description | | ------------------- | ------- | ------------------------------------------ | | `name` | string | Full network name (e.g., Ethereum Mainnet) | | `shortName` | string | Network abbreviation (e.g., ETH) | | `memoNeeded` | boolean | Indicates memo/extra ID requirement | | `addressValidation` | string | Regex pattern for address validation | | `explorerUrl` | string | Block explorer URL | | `addressUrl` | string | Address explorer URL | | `priority` | number | Network priority ranking | | `kind` | string | Network type (e.g., `evm`) | | `chainId` | number | Blockchain network ID | # Get DEX Quote Source: https://docs.houdiniswap.com/api-v1/get-dex-quote Get a price quote for a DEX swap Deprecation of the REST v1 swap endpoints begins on September 30, 2026. Please migrate to the Partner API v2. Dashboard and authentication endpoints remain on v1 GraphQL and are not affected. [Read the migration guide](https://docs.houdiniswap.com/migration/v1-to-v2) # Get DEX Quote ## Endpoint ```text theme={null} GET /dexQuote ``` Returns pricing and routing details for a DEX swap. ## Query Parameters | Field | Type | Description | | ------------- | ------ | -------------------------------------------------------------- | | `amount` | string | Amount to swap (e.g., `100`) | | `tokenIdFrom` | string | ID of the source token (e.g., `6689b73ec90e45f3b3e51553`) | | `tokenIdTo` | string | ID of the destination token (e.g., `6689b73ec90e45f3b3e51558`) | ## Example Request ```text theme={null} GET /dexQuote?amount=100&tokenIdFrom=6689b73ec90e45f3b3e51553&tokenIdTo=6689b73ec90e45f3b3e51558 ``` ## Example Response ```json theme={null} [ { "swap": "sw", "quoteId": "66fa78f90bf604337992cba9", "amountOut": 16.76282693, "amountOutUsd": 2589.019, "duration": 1, "gas": 5978057870193888, "feeUsd": 4.853, "path": ["debridge"], "raw": { "duration": 1, "gas": "5978057870193888", "quote": { "integration": "debridge", "type": "swap", "bridgeFee": "31425535", "bridgeFeeInNativeToken": "1000000000000000", "amount": "16762826930", "decimals": 9, "amountUSD": "2589.019", "bridgeFeeUSD": "4.853", "bridgeFeeInNativeTokenUSD": "2.606", "fees": [ { "type": "bridge", "amount": "31425535", "amountUSD": "4.853", "chainSlug": "solana", "tokenSymbol": "SOL", "tokenAddress": "11111111111111111111111111111111", "decimals": 9, "deductedFromSourceToken": true }, { "type": "bridge", "amount": "1000000000000000", "amountUSD": "2.606", "chainSlug": "ethereum", "tokenSymbol": "ETH", "tokenAddress": "0x0000000000000000000000000000000000000000", "decimals": 18, "deductedFromSourceToken": false }, { "type": "gas", "amount": "5978057870193888", "amountUSD": "15.582", "chainSlug": "ethereum", "tokenSymbol": "ETH", "tokenAddress": "0x0000000000000000000000000000000000000000", "decimals": 18, "deductedFromSourceToken": false }, { "type": "partner", "amount": "2000000000000000", "amountUSD": "5.213", "chainSlug": "ethereum", "tokenSymbol": "ETH", "tokenAddress": "0x0000000000000000000000000000000000000000", "decimals": 18, "deductedFromSourceToken": true } ] }, "route": [ { "bridge": "debridge", "bridgeTokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "steps": ["allowance", "approve", "send"], "name": "USDC", "part": 100 } ], "distribution": { "debridge": 1 }, "gasUSD": "15.582" } } ] ``` ## Response Fields | Field | Type | Description | | -------------- | ------ | ------------------------------------------------------------------------------------------------------- | | `swap` | string | Swap identifier | | `quoteId` | string | Unique quote ID (use in `dexExchange`) | | `amountOut` | number | Quoted output amount | | `amountOutUsd` | number | Quoted output amount in USD | | `duration` | number | Estimated swap duration in minutes | | `gas` | number | Gas fee for the swap | | `feeUsd` | number | Fee in USD | | `path` | array | Bridge/routing path | | `raw` | object | Raw transaction data including gas and duration. See [RouteDTO](https://api-partner.houdiniswap.com/#/) | # Get DEX Tokens Source: https://docs.houdiniswap.com/api-v1/get-dex-tokens Retrieve supported DEX token list Deprecation of the REST v1 swap endpoints begins on September 30, 2026. Please migrate to the Partner API v2. Dashboard and authentication endpoints remain on v1 GraphQL and are not affected. [Read the migration guide](https://docs.houdiniswap.com/migration/v1-to-v2) # Get DEX Tokens ## Endpoint ```text theme={null} GET /dexTokens ``` Returns a paginated list of tokens supported for decentralized exchange (DEX) swaps. ## Query Parameters | Field | Type | Default | Description | | ---------- | ------ | ------- | ---------------------------------------- | | `page` | number | `1` | Page number to retrieve | | `pageSize` | number | `100` | Number of tokens per page | | `chain` | string | — | Blockchain network filter (e.g., `base`) | ## Example Request ```text theme={null} GET /dexTokens?page=1&pageSize=100&chain=base ``` ## Example Response ```json theme={null} { "count": 1, "tokens": [ { "id": "66cf5512ba629b6c861a7f45", "address": "0xE3086852A4B125803C815a158249ae468A3254Ca", "chain": "base", "decimals": 18, "symbol": "mfer", "name": "mfer", "created": "2024-08-28T10:00:00.000Z", "modified": "2024-08-28T10:00:00.000Z", "enabled": true, "hasDex": true } ] } ``` ## Response Fields | Field | Type | Description | | -------- | ------ | ------------------------------- | | `count` | number | Total number of matching tokens | | `tokens` | array | Array of token objects | ### Token Object | Field | Type | Description | | ---------- | ------- | ------------------------------------ | | `id` | string | Unique token identifier | | `address` | string | Token contract address | | `chain` | string | Network designation (e.g., `base`) | | `decimals` | number | Token decimal precision | | `symbol` | string | Token symbol (e.g., `mfer`) | | `name` | string | Full token name | | `created` | string | ISO 8601 timestamp of addition | | `modified` | string | ISO 8601 timestamp of last update | | `enabled` | boolean | Whether the token is active | | `hasDex` | boolean | Whether the token supports DEX swaps | # Get Min/Max Source: https://docs.houdiniswap.com/api-v1/get-min-max Get minimum and maximum exchange amounts for a pair Deprecation of the REST v1 swap endpoints begins on September 30, 2026. Please migrate to the Partner API v2. Dashboard and authentication endpoints remain on v1 GraphQL and are not affected. [Read the migration guide](https://docs.houdiniswap.com/migration/v1-to-v2) # Get Min/Max ## Endpoint ```text theme={null} GET /getMinMax ``` Returns the minimum and maximum exchangeable amounts for a given token pair. ## Query Parameters | Field | Type | Required | Description | | ----------- | ------- | -------- | --------------------------------------------- | | `from` | string | Yes | Symbol of the source token (e.g., `ETH`) | | `to` | string | Yes | Symbol of the destination token (e.g., `BNB`) | | `anonymous` | boolean | Yes | Whether to check limits for anonymous routing | | `cexOnly` | boolean | No | Limit results to centralized exchanges only | ## Example Request ```text theme={null} GET /getMinMax?from=ETH&to=BNB&anonymous=false&cexOnly=false ``` ## Example Response ```json theme={null} [ 0.0253712625, 16.914175 ] ``` ## Response Returns an array of two numbers: | Index | Description | | ----- | --------------------------- | | `[0]` | Minimum exchangeable amount | | `[1]` | Maximum exchangeable amount | # Get Status Source: https://docs.houdiniswap.com/api-v1/get-status Check the status of a swap transaction Deprecation of the REST v1 swap endpoints begins on September 30, 2026. Please migrate to the Partner API v2. Dashboard and authentication endpoints remain on v1 GraphQL and are not affected. [Read the migration guide](https://docs.houdiniswap.com/migration/v1-to-v2) # Get Status ## Endpoint ```text theme={null} GET /status/ ``` Returns the current status of a swap transaction. ## Path Parameters | Field | Type | Description | | ----- | ------ | -------------------------------------------------------------------------------------- | | `id` | string | Unique ID of the transaction (from exchange response, example: h9NpKm75gRnX7GWaFATwYn) | ## Example Request ```text theme={null} GET /status?id=fgCqqMztaiV8dyo6mLqYgx ``` ## Status Codes | Code | Status | Description | | ---- | ----------- | ---------------------------------------------- | | `-1` | NEW | Transaction has been created | | `0` | WAITING | Waiting for funds to be received | | `1` | CONFIRMING | Funds received, confirming | | `2` | EXCHANGING | Exchange in progress | | `3` | ANONYMIZING | Privacy routing in progress | | `4` | FINISHED | Transaction completed successfully | | `5` | EXPIRED | Transaction expired before funds were received | | `6` | FAILED | Transaction failed | | `7` | REFUNDED | Funds were refunded | | `8` | DELETED | Transaction was deleted | # Get Volume Source: https://docs.houdiniswap.com/api-v1/get-volume Retrieve total swap volume for HoudiniSwap Deprecation of the REST v1 swap endpoints begins on September 30, 2026. Please migrate to the Partner API v2. Dashboard and authentication endpoints remain on v1 GraphQL and are not affected. [Read the migration guide](https://docs.houdiniswap.com/migration/v1-to-v2) # Get Volume ## Endpoint ```text theme={null} GET /volume ``` Retrieves the total swap volume for HoudiniSwap. ## Parameters None required. ## Example Request ```text theme={null} GET /volume ``` ## Example Response ```json theme={null} [ { "count": 0, "totalTransactedUSD": 0 } ] ``` ## Response Fields | Field | Type | Description | | -------------------- | ------ | ------------------------------------------------------ | | `count` | number | Total number of transactions on the exchange | | `totalTransactedUSD` | number | Total value transacted in USD at the time of each swap | # Get Weekly Volume Source: https://docs.houdiniswap.com/api-v1/get-weekly-volume Retrieve weekly swap volume data for HoudiniSwap Deprecation of the REST v1 swap endpoints begins on September 30, 2026. Please migrate to the Partner API v2. Dashboard and authentication endpoints remain on v1 GraphQL and are not affected. [Read the migration guide](https://docs.houdiniswap.com/migration/v1-to-v2) # Get Weekly Volume ## Endpoint ```text theme={null} GET /weeklyVolume ``` Retrieves weekly swap volume data for HoudiniSwap, organized by week and year. ## Parameters None required. ## Example Request ```text theme={null} GET /weeklyVolume ``` ## Example Response ```json theme={null} [ { "count": 0, "anonymous": 0, "volume": 0, "week": 0, "year": 0, "commission": 0 } ] ``` ## Response Fields | Field | Type | Description | | ------------ | ------ | ----------------------------------------------------- | | `count` | number | Total transactions for the week | | `anonymous` | number | Number of anonymous transactions (out of total count) | | `volume` | number | Total transaction volume in USD | | `week` | number | Week number of the year | | `year` | number | Year | | `commission` | number | Commission earned in USD | # API v1 Introduction Source: https://docs.houdiniswap.com/api-v1/introduction HoudiniSwap Partner API documentation Deprecation of the REST v1 swap endpoints begins on September 30, 2026. Please migrate to the Partner API v2. Dashboard and authentication endpoints remain on v1 GraphQL and are not affected. [Read the migration guide](https://docs.houdiniswap.com/migration/v1-to-v2) # API v1 **API v1 is for existing integrators only.** If you are building a new integration, use [API v2](/developer-hub/getting-started/authentication) — it offers unified endpoints, improved token handling, and WebSocket order updates. Never expose your API key and secret publicly. This API is meant to be used in a **backend environment**, not a frontend UI directly. ## Overview The HoudiniSwap Partner API enables you to integrate private cryptocurrency swaps into your platform. The API supports both CEX (centralized) and DEX (decentralized) swap flows. ## Base URL ```text theme={null} https://api-partner.houdiniswap.com/ ``` ## Authentication All requests require an `Authorization` header using the format: ```text theme={null} Authorization: : ``` ## Mandatory Fields All exchange requests must include the following fields for compliance: | Field | Description | | ----------- | ----------------------------- | | `ip` | User's IP address | | `userAgent` | Browser user agent string | | `timezone` | User's timezone (e.g., `UTC`) | ## Core Workflow 1. **Authentication** – Verify your API credentials 2. **Get Currencies** – Retrieve supported tokens and network information 3. **Get Quote** – Obtain pricing data for a swap, including exchange rates and validity windows 4. **Initiate Exchange** – Execute the transaction with source/destination currencies and recipient address 5. **Get Status** – Monitor swap progress from initiation through completion # POST CEX Exchange Source: https://docs.houdiniswap.com/api-v1/post-cex-exchange Initiates an exchange transaction, including sender and receiver information, transaction status, quoted amounts, and token details. Deprecation of the REST v1 swap endpoints begins on September 30, 2026. Please migrate to the Partner API v2. Dashboard and authentication endpoints remain on v1 GraphQL and are not affected. [Read the migration guide](https://docs.houdiniswap.com/migration/v1-to-v2) # POST CEX Exchange Initiates an exchange transaction, including sender and receiver information, transaction status, quoted amounts, and token details. This information is useful for tracking and managing token exchange. ## Endpoint ```text theme={null} POST /exchange ``` **Request Body:** A JSON object containing `Exchange` details. The `ip`, `userAgent`, and `timezone` fields are mandatory for compliance purposes. ## Request Fields | Field | Type | Required | Description | | ------------- | ------- | -------- | -------------------------------------------------------------------------------- | | `amount` | number | Yes | Amount to be exchanged (example: `1`) | | `from` | string | Yes | Symbol of the input token (example: `ETH`) | | `to` | string | Yes | Symbol of the output token (example: `BNB`) | | `addressTo` | string | Yes | Destination address | | `anonymous` | boolean | Yes | Indicates if the transaction is anonymous (example: `false`) | | `ip` | string | Yes | User IP address. Used for fraud prevention only | | `userAgent` | string | Yes | User userAgent browser string | | `timezone` | string | Yes | User browser timezone (example: `UTC`) | | `receiverTag` | string | No | Optional receiver tag (example: `123`) | | `walletId` | string | No | User's wallet identifier | | `useXmr` | string | No | Use XMR if `true`, or use another token for the anonymous transaction if `false` | | `filters` | object | No | Rotation and provider filtering options. See below | ### Filters Object | Field | Type | Default | Description | | --------------------- | --------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `rotatePayoutWallets` | boolean | `false` | Enable payout wallet rotation. Deprioritizes recently-used provider paths | | `deviationThreshold` | number | `5` | Maximum price deviation percentage allowed when rotating | | `rotationLookback` | number | `10` | Number of recent orders to check for path deduplication | | `onlySwaps` | string\[] | all | Restrict to specific providers (e.g., `["cl", "ss"]`). See [Provider Codes](/developer-hub/core-concepts/payout-wallet-rotation#how-it-works) | When `rotatePayoutWallets` is enabled, any provided `inQuoteId` and `outQuoteId` are discarded. The system forces a fresh re-quote with rotation applied to ensure the new path is used. ## Example Request ```json theme={null} POST /exchange Content-Type: application/json { "amount": 1, "from": "ETH", "to": "BNB", "addressTo": "0x000000000000000000000000000000000000dead", "anonymous": true, "ip": "0.0.0.0", "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "timezone": "UTC", "useXmr": false, "filters": { "rotatePayoutWallets": true, "deviationThreshold": 5, "rotationLookback": 10 } } ``` ## Example Response ```json theme={null} { "houdiniId": "mNev2KrGikabTvge75GMwM", "created": "2025-02-21T15:32:53.821Z", "senderAddress": "0xabb5ac5c686d1614172ac1292519e78ad6c520f5", "receiverAddress": "0x000000000000000000000000000000000000dead", "anonymous": false, "expires": "2025-02-21T16:02:53.821Z", "status": 0, "inAmount": 1, "inSymbol": "ETH", "outAmount": 4.189816, "outSymbol": "BNB", "senderTag": "", "receiverTag": "", "notified": false, "eta": 5, "inAmountUsd": 2802.42, "inCreated": "2025-02-21T15:32:53.822Z", "quote": { "amountIn": 1, "amountOut": 4.189816, "min": 0.001, "max": 17.841, "path": "ff" }, "outToken": { "id": "USDT", "name": "Tether", "symbol": "USDT", "network": { "name": "Ethereum Mainnet", "shortName": "ETH", "addressValidation": "^(0x)[0-9A-Fa-f]{40}$", "memoNeeded": false, "explorerUrl": "https://etherscan.io/tx/", "addressUrl": "https://etherscan.io/address/", "priority": 1, "kind": "evm", "chainId": 1 }, "color": "#26a17b", "keyword": "usdt usdterc20 tether usdteth erc-20 erc20 eth", "displayName": "USDT (ETHEREUM)", "chain": 1, "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", "hasMarkup": true, "networkPriority": 2, "icon": "https://...", "hasFixed": true, "hasFixedReverse": true }, "inToken": { "id": "BNB", "name": "BNB", "symbol": "BNB", "network": { "name": "Binance Smart Chain", "shortName": "BSC", "addressValidation": "^(0x)[0-9A-Za-z]{40}$", "memoNeeded": false, "explorerUrl": "https://bscscan.com/tx/", "addressUrl": "https://bscscan.com/address/", "priority": 6, "kind": "evm", "chainId": 56, "icon": "http://127.0.0.1:3000/assets/networks/BSC.png" }, "color": "#FFFFFF", "keyword": "BNB BNB Binance Smart Chain evm BNB BNB bsc Binance Smart Chain", "displayName": "BNB (BSC)", "chain": 56, "networkPriority": 1, "icon": "http://127.0.0.1:3000/assets/tokens/BNB-BSC.png", "hasFixed": false, "hasFixedReverse": false } } ``` ## Response Fields | Field | Type | Description | | ----------------- | ------- | ----------------------------------------------------------------------------------- | | `houdiniId` | string | Unique identifier for the exchange transaction | | `created` | string | Timestamp of when the transaction was created | | `senderAddress` | string | **Address to send funds to** | | `receiverAddress` | string | Address of the receiver | | `anonymous` | boolean | Indicates if the transaction is anonymous | | `expires` | string | Expiration timestamp of the transaction | | `status` | number | Status code of the transaction | | `inAmount` | number | Amount sent in the exchange | | `outAmount` | number | Amount received in the exchange | | `inSymbol` | string | Symbol of the input token | | `outSymbol` | string | Symbol of the output token | | `senderTag` | string | *(Optional)* Sender tag | | `receiverTag` | string | *(Optional)* Receiver tag | | `notified` | boolean | Indicates if the user has been notified | | `eta` | number | Estimated time of arrival (in minutes) | | `inAmountUsd` | number | Input amount converted to USD | | `inCreated` | string | Timestamp when the input was created | | `quote` | object | Details of the exchange quote (amounts, min/max limits, path). See QuoteDTO | | `outToken` | object | Details of the output token. See [TokenDTO](https://api-partner.houdiniswap.com/#/) | | `inToken` | object | Details of the input token. See [TokenDTO](https://api-partner.houdiniswap.com/#/) | After receiving the response, send the exact `inAmount` of the `inSymbol` token to the `senderAddress` to initiate the swap. # POST DEX Approve Source: https://docs.houdiniswap.com/api-v1/post-dex-approve Get approval transaction data for a DEX swap Deprecation of the REST v1 swap endpoints begins on September 30, 2026. Please migrate to the Partner API v2. Dashboard and authentication endpoints remain on v1 GraphQL and are not affected. [Read the migration guide](https://docs.houdiniswap.com/migration/v1-to-v2) # POST DEX Approve ## Endpoint ```text theme={null} POST /dexApprove ``` Returns the approval transaction data needed before executing a DEX swap. ## Request Body ```json theme={null} { "tokenIdTo": "6689b73ec90e45f3b3e51558", "tokenIdFrom": "6689b73ec90e45f3b3e51553", "addressFrom": "0x45CF73349a4895fabA18c0f51f06D79f0794898D", "amount": 1, "swap": "sw", "route": { "duration": 1, "gas": "5387746374601800", "quote": { "integration": "debridge", "type": "swap", "bridgeFee": "29257969", "bridgeFeeInNativeToken": "1000000000000000", "amount": "657693444", "decimals": 9, "amountUSD": "94.905", "bridgeFeeUSD": "4.221", "bridgeFeeInNativeTokenUSD": "2.434", "fees": [ { "type": "bridge", "amount": "29257969", "amountUSD": "4.221", "chainSlug": "solana", "tokenSymbol": "SOL", "tokenAddress": "11111111111111111111111111111111", "decimals": 9, "deductedFromSourceToken": true }, { "type": "bridge", "amount": "1000000000000000", "amountUSD": "2.434", "chainSlug": "ethereum", "tokenSymbol": "ETH", "tokenAddress": "0x0000000000000000000000000000000000000000", "decimals": 18, "deductedFromSourceToken": false }, { "type": "gas", "amount": "5387746374601800", "amountUSD": "13.118", "chainSlug": "ethereum", "tokenSymbol": "ETH", "tokenAddress": "0x0000000000000000000000000000000000000000", "decimals": 18, "deductedFromSourceToken": false }, { "type": "partner", "amount": "200000", "amountUSD": "0.200", "chainSlug": "ethereum", "tokenSymbol": "USDT", "tokenAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7", "decimals": 6, "deductedFromSourceToken": true } ] }, "route": [ { "bridge": "debridge", "bridgeTokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "steps": ["allowance", "approve", "send"], "name": "USDC", "part": 100 } ], "distribution": { "debridge": 1 }, "gasUSD": "13.118" } } ``` ## Request Fields | Field | Type | Description | | ------------- | ------ | ------------------------------------------------ | | `tokenIdTo` | string | Destination token identifier | | `tokenIdFrom` | string | Source token identifier | | `addressFrom` | string | Address from which the amount will be deducted | | `amount` | number | Approval amount | | `swap` | string | Swap type identifier (e.g., `sw`) | | `route` | object | Routing configuration (RouteDTO from `dexQuote`) | ## Example Response ```json theme={null} [ { "data": "0x095ea7b300000000000000000000000072788af7fc87d14da73f94e353e52b76e230035b000000000000000000000000000000000000000000000000016345785d8a0000", "to": "0x0000000000000000000000000000000000000000", "from": "0x789", "fromChain": { "id": "667160411933f7647414f091", "created": "2024-06-18T10:24:01.870Z", "modified": "2024-09-04T13:41:54.131Z", "name": "Ethereum", "shortName": "ethereum", "addressValidation": "^(0x)[0-9A-Fa-f]{40}$", "explorerUrl": "https://etherscan.io/tx/{txHash}", "addressUrl": "https://etherscan.io/tokens/{address}", "kind": "evm", "chainId": 1, "block": "0", "gasPrice": "0", "lastBaseFeePerGas": "0", "maxFeePerGas": "0", "maxPriorityFeePerGas": "0", "enabled": true, "icon": "http://localhost:3000/assets/dexchains/667160411933f7647414f091.png" } } ] ``` ## Response Fields | Field | Type | Description | | ----------- | ------ | ------------------------------------------------- | | `data` | string | Encoded transaction data (hex) to submit on-chain | | `to` | string | Target contract address | | `from` | string | Source token address | | `fromChain` | object | Source chain details (ChainDTO) | ### ChainDTO Fields | Field | Type | Description | | ---------------------- | ------- | ---------------------------- | | `id` | string | Unique chain identifier | | `created` | string | Creation timestamp | | `modified` | string | Last modification timestamp | | `name` | string | Chain full name | | `shortName` | string | Chain abbreviation | | `addressValidation` | string | Regex for address validation | | `memoNeeded` | boolean | Whether memo is required | | `explorerUrl` | string | Block explorer URL format | | `addressUrl` | string | Token address URL format | | `icon` | string | Chain icon URL | | `kind` | string | Chain type (e.g., `evm`) | | `chainId` | number | Blockchain network ID | | `block` | bigint | Current block number | | `gasPrice` | bigint | Current gas price | | `lastBaseFeePerGas` | bigint | Recent base fee | | `maxFeePerGas` | bigint | Maximum fee ceiling | | `maxPriorityFeePerGas` | bigint | Priority fee limit | | `enabled` | boolean | Whether chain is active | | `shortNameV1` | string | Legacy chain abbreviation | # POST DEX Confirm Transaction Source: https://docs.houdiniswap.com/api-v1/post-dex-confirm-tx Confirm a DEX transaction after on-chain submission Deprecation of the REST v1 swap endpoints begins on September 30, 2026. Please migrate to the Partner API v2. Dashboard and authentication endpoints remain on v1 GraphQL and are not affected. [Read the migration guide](https://docs.houdiniswap.com/migration/v1-to-v2) # POST DEX Confirm Transaction ## Endpoint ```text theme={null} POST /dexConfirmTx ``` Confirms a DEX transaction after the approval transaction has been submitted on-chain. Returns a boolean indicating success. ## Request Body ```json theme={null} { "id": "6689b73ec90e45f3b3e51553", "txHash": "0x123456789abcdef..." } ``` ## Request Fields | Field | Type | Description | | -------- | ------ | -------------------------------------------------------- | | `id` | string | Internal ID of the transaction | | `txHash` | string | Blockchain transaction hash from the on-chain submission | ## Example Response ```json theme={null} true ``` ## Response Fields | Field | Type | Description | | ---------- | ------- | ---------------------------------------------------- | | `response` | boolean | `true` if the transaction was successfully confirmed | # POST DEX Exchange Source: https://docs.houdiniswap.com/api-v1/post-dex-exchange Initiate a DEX cryptocurrency swap Deprecation of the REST v1 swap endpoints begins on September 30, 2026. Please migrate to the Partner API v2. Dashboard and authentication endpoints remain on v1 GraphQL and are not affected. [Read the migration guide](https://docs.houdiniswap.com/migration/v1-to-v2) # POST DEX Exchange ## Endpoint ```text theme={null} POST /dexExchange ``` Initiates a DEX swap. Returns transaction metadata including the on-chain transaction data to submit. ## Request Body ```json theme={null} { "amount": 0.2, "tokenIdTo": "6689b73ec90e45f3b3e51558", "tokenIdFrom": "6689b73ec90e45f3b3e51553", "addressTo": "H1DiPSsBVBpDG57q5ZnxhZpRrsPQBvZfrbFQth6wyGyw", "addressFrom": "0x45CF73349a4895fabA18c0f51f06D79f0794898D", "swap": "sw", "quoteId": "66fa79723eccf00d849b48ed", "route": { "duration": 1, "gas": "5387746374601800", "quote": { "integration": "debridge", "type": "swap", "bridgeFee": "29257969", "bridgeFeeInNativeToken": "1000000000000000", "amount": "657693444", "decimals": 9, "amountUSD": "94.905", "bridgeFeeUSD": "4.221", "bridgeFeeInNativeTokenUSD": "2.434", "fees": [ { "type": "bridge", "amount": "29257969", "amountUSD": "4.221", "chainSlug": "solana", "tokenSymbol": "SOL", "tokenAddress": "11111111111111111111111111111111", "decimals": 9, "deductedFromSourceToken": true }, { "type": "bridge", "amount": "1000000000000000", "amountUSD": "2.434", "chainSlug": "ethereum", "tokenSymbol": "ETH", "tokenAddress": "0x0000000000000000000000000000000000000000", "decimals": 18, "deductedFromSourceToken": false }, { "type": "gas", "amount": "5387746374601800", "amountUSD": "13.118", "chainSlug": "ethereum", "tokenSymbol": "ETH", "tokenAddress": "0x0000000000000000000000000000000000000000", "decimals": 18, "deductedFromSourceToken": false }, { "type": "partner", "amount": "200000", "amountUSD": "0.200", "chainSlug": "ethereum", "tokenSymbol": "USDT", "tokenAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7", "decimals": 6, "deductedFromSourceToken": true } ] }, "route": [ { "bridge": "debridge", "bridgeTokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "steps": ["allowance", "approve", "send"], "name": "USDC", "part": 100 } ], "distribution": { "debridge": 1 }, "gasUSD": "13.118", "useXmr": false, "deviceInfo": "MacOS", "isMobile": false } } ``` ## Request Fields | Field | Type | Description | | ------------- | ------ | ------------------------------------------------------------------ | | `amount` | number | Amount to exchange (e.g., `0.2`) | | `tokenIdTo` | string | ID of the output token | | `tokenIdFrom` | string | ID of the input token | | `addressTo` | string | Destination wallet address | | `addressFrom` | string | Sender's wallet address | | `swap` | string | Swap method identifier (e.g., `sw`) | | `quoteId` | string | Quote ID from `dexQuote` response | | `route` | object | Full route object from `dexQuote` raw response. See RouteDTO below | ### RouteDTO Fields | Field | Type | Description | | -------------- | ------- | ----------------------------------------------- | | `duration` | number | Estimated duration of the swap in minutes | | `gas` | string | Gas fee for the swap transaction | | `quote` | object | Quote data for the swap. See QuoteDataDTO below | | `route` | array | Array of bridge route objects | | `distribution` | object | Distribution of the swap across bridges | | `gasUSD` | string | Gas fee in USD | | `useXmr` | boolean | Whether XMR is used for anonymous routing | | `deviceInfo` | string | Device information (example: `MacOS`) | | `isMobile` | boolean | Whether the client is a mobile device | ### QuoteDataDTO Fields | Field | Type | Description | | --------------------------- | ------ | --------------------------------------------------------- | | `integration` | string | Integration name (example: `debridge`) | | `type` | string | Type of exchange (example: `swap`) | | `bridgeFee` | string | Fee for bridge transactions (example: `29257969`) | | `bridgeFeeInNativeToken` | string | Bridge fee in native tokens (example: `1000000000000000`) | | `amount` | string | Amount involved in the exchange (example: `657693444`) | | `decimals` | number | Number of decimals for precision (example: `9`) | | `amountUSD` | string | Amount converted to USD (example: `94.905`) | | `bridgeFeeUSD` | string | Bridge fee in USD (example: `4.221`) | | `bridgeFeeInNativeTokenUSD` | string | Bridge fee in native token converted to USD | | `fees` | array | Array of fee objects (bridge, gas, partner fees) | ## Example Response ```json theme={null} { "houdiniId": "h9NpKm75gRnX7GWaFATwYn", "created": "2024-10-08T12:22:25.843Z", "senderAddress": "0xe90cAc99ccab34A669fFC2eE4e9c0E5067dE29ac", "receiverAddress": "H1DiPSsBVBpDG57q5ZnxhZpRrsPQBvZfrbFQth6wyGyw", "anonymous": false, "expires": "2024-10-08T12:52:25.843Z", "status": 0, "inAmount": 50, "inSymbol": "6689b73ec90e45f3b3e51592", "outAmount": 0.266471241, "outSymbol": "6689b73ec90e45f3b3e51558", "senderTag": "", "receiverTag": "", "notified": false, "eta": 1, "inAmountUsd": 38.422, "inCreated": "2024-10-08T12:22:25.843Z", "quote": { "amountIn": 50, "amountOut": 0.266471241 }, "metadata": { "from": "0xe90cAc99ccab34A669fFC2eE4e9c0E5067dE29ac", "to": "0xD0c0bA0b6F151729f4BacB40b8Bb8047360b1ad6", "data": "0x601e0bbe...", "value": "0x11c37937e08000", "txId": "0x2c0d17c9ecde56ec1604582abfe102c331c235be9da45aa266519080d3e9d10d", "gas": "0x0699bc" }, "isDex": true } ``` ## Response Fields | Field | Type | Description | | ----------------- | ------- | -------------------------------------- | | `houdiniId` | string | Unique transaction identifier | | `created` | string | Timestamp when transaction was created | | `senderAddress` | string | Sender's wallet address | | `receiverAddress` | string | Recipient's wallet address | | `anonymous` | boolean | Whether transaction is anonymous | | `expires` | string | Expiration timestamp | | `status` | number | Status code | | `inAmount` | number | Input amount | | `inSymbol` | string | Input token ID | | `outAmount` | number | Output amount | | `outSymbol` | string | Output token ID | | `eta` | number | Estimated time in minutes | | `inAmountUsd` | number | Input amount in USD | | `inCreated` | string | Timestamp when input was created | | `quote` | object | Quoted exchange details | | `metadata` | object | On-chain transaction data to submit | | `isDex` | boolean | `true` for DEX transactions | ### metadata Object | Field | Type | Description | | ------- | ------ | ------------------------ | | `from` | string | Sender address | | `to` | string | Contract address to call | | `data` | string | Encoded calldata (hex) | | `value` | string | ETH value to send (hex) | | `txId` | string | Transaction ID | | `gas` | string | Gas limit (hex) | # DEX Swap Integration API v1 Source: https://docs.houdiniswap.com/api-v1/swap-flows/dex-swap Complete guide to integrating on-chain DEX swaps with wallet signatures and approvals Deprecation of the REST v1 swap endpoints begins on September 30, 2026. Please migrate to the Partner API v2. Dashboard and authentication endpoints remain on v1 GraphQL and are not affected. [Read the migration guide](https://docs.houdiniswap.com/migration/v1-to-v2) ## Overview DEX (Decentralized Exchange) swaps execute on-chain through smart contracts. Unlike CEX swaps that use deposit addresses, DEX swaps require users to connect their wallet, sign transactions, and broadcast them directly to the blockchain. **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. ### Supported Networks DEX swaps are currently supported on the following networks: * **EVM** (Ethereum, BSC, Polygon, etc.) * **Solana** * **SUI** * **TRON** * **TON** * **Stellar** **Stellar Trustline Requirement**: For swaps involving Stellar assets, the integrator is responsible for ensuring that the destination account has the required [trustlines](https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/accounts#trustlines) established before initiating the swap. Houdini does not handle trustline creation automatically. If the trustline is missing, the swap will fail. ## Key Characteristics | Feature | DEX | CEX (Private/Standard) | | ------------- | -------------------------------------- | ----------------------------- | | **Execution** | On-chain via smart contracts | Off-chain via exchanges | | **Wallet** | Required (MetaMask, etc.) | Not required | | **Custody** | User keeps custody | User sends to deposit address | | **Speed** | Varies by network (seconds to minutes) | 3-45 minutes | | **Approvals** | May require token approvals | Not required | ## How It Works DEX swaps follow this flow: ```mermaid theme={null} sequenceDiagram participant User participant YourApp participant Wallet participant DEX participant Blockchain User->>YourApp: Request swap YourApp->>API: Get quote API-->>YourApp: Return route YourApp->>API: Check approvals API-->>YourApp: Return approval/signature requirements User->>Wallet: Send approval transactions (if needed) Wallet->>Blockchain: Submit approval User->>Wallet: Sign signatures (if needed) Wallet->>DEX: Execute swap DEX->>Blockchain: Process on-chain Blockchain-->>YourApp: Transaction confirmed ``` ## Technical Flow Diagram This diagram shows the complete integration flow with conditional logic: ```mermaid theme={null} flowchart TD Start([Start Integration]) --> Step1[Step 1: Get Supported Assets
/dexTokens, /networks] Step1 --> Step2[Step 2: Get DEX Quote
/dexQuote] Step2 --> Step3[Step 3: Check Approvals & Signatures
/dexApprove] Step3 --> CheckArrays{What's needed?} CheckArrays -->|Has approvals| Step4[Step 4: Send Approval Transactions
Broadcast via wallet] CheckArrays -->|Has signatures only| Step5[Step 5: Process Signatures
User signs with wallet] CheckArrays -->|Has both| Step4 CheckArrays -->|Neither| Step7[Step 7: Execute Swap
/dexExchange] Step4 --> CheckSig{Has signatures?} CheckSig -->|Yes| Step5 CheckSig -->|No| Step6[Step 6: Check Allowance
/dexHasEnoughAllowance] Step5 --> CheckSigType{Signature type?} CheckSigType -->|SINGLE| CollectSig[Collect signature] CheckSigType -->|CHAINED| ChainLoop[Loop: Sign → /chainSignatures
Until isComplete = true] CollectSig --> CheckApproval{Sent approvals?} ChainLoop --> CheckApproval CheckApproval -->|Yes| Step6 CheckApproval -->|No| Step7 Step6 --> PollAllowance{hasEnoughAllowance?} PollAllowance -->|No| Wait[Wait 30 seconds] Wait --> PollAllowance PollAllowance -->|Yes| Step7 Step7 --> CheckOffChain{offChain?} CheckOffChain -->|false| BroadcastTx[Broadcast transaction
via wallet] CheckOffChain -->|true| ConfirmOffChain[Call /dexConfirmTx
with txHash: undefined] BroadcastTx --> ConfirmTx[Call /dexConfirmTx
with txHash] ConfirmTx --> Step8[Step 8: Track Status
/status] ConfirmOffChain --> Step8 Step8 --> PollStatus{Status?} PollStatus -->|0-2| WaitStatus[Wait 30 seconds] WaitStatus --> Step8 PollStatus -->|4 COMPLETED| Success([✅ Swap Complete]) PollStatus -->|6 FAILED| Failed([❌ Swap Failed]) style Step1 fill:#e1f5ff,color:#000 style Step2 fill:#e1f5ff,color:#000 style Step3 fill:#fff4e1,color:#000 style Step4 fill:#ffe1e1,color:#000 style Step5 fill:#ffe1e1,color:#000 style Step6 fill:#ffe1e1,color:#000 style Step7 fill:#e1ffe1,color:#000 style Step8 fill:#f0e1ff,color:#000 style Success fill:#90EE90,color:#000 style Failed fill:#FFB6C1,color:#000 ``` ## Integration Steps ### Step 1: Get Supported Assets Discover which tokens and networks are available for DEX swaps. Learn more about [DEX tokens](/developer-hub/core-concepts/tokens-networks#dex-tokens) and [network identifiers](/developer-hub/core-concepts/tokens-networks#network-identifiers). **Performance Note**: The `/tokens` endpoint can take several seconds to minute to respond due to the large token list. * Save tokens in your backend database * Load tokens on server startup or via scheduled job * Serve token list from your database to frontend * Refresh cache periodically (e.g., every 24 hours) ```javascript JavaScript theme={null} const tokens = await fetchFromHoudini('/dexTokens'); // Get supported networks const networks = await fetchFromHoudini('/networks'); ``` ```bash cURL theme={null} # Get tokens curl -X GET "https://api-partner.houdiniswap.com/dexTokens" \ -H "Authorization: your_api_key:your_api_secret" # Get networks curl -X GET "https://api-partner.houdiniswap.com/networks" \ -H "Authorization: your_api_key:your_api_secret" ``` ### Step 2: Get DEX Quote Request a quote for the DEX swap using token IDs from [`/dexTokens`](/developer-hub/core-concepts/tokens-networks#fetch-dex-tokens): ```javascript JavaScript theme={null} const quotes = await fetchFromHoudini('/dexQuote', { tokenIdFrom: '6689b73ec90e45f3b3e51566', // ETH token _id from /dexTokens tokenIdTo: '6689b73ec90e45f3b3e51553', // USDT token _id from /dexTokens amount: 1, }); // Select the best quote (first in array) const selectedQuote = quotes[0]; ``` ```bash cURL theme={null} curl -X GET "https://api-partner.houdiniswap.com/dexQuote?tokenIdFrom=6689b73ec90e45f3b3e51558&tokenIdTo=6689b73ec90e45f3b3e51553&amount=1&address=0x742d35Cc6634C0532925a3b844Bc454e4438f44e" \ -H "Authorization: your_api_key:your_api_secret" ``` **Quote Parameters:** * `tokenIdFrom`: Source token `_id` from [`/dexTokens`](/developer-hub/core-concepts/tokens-networks#fetch-dex-tokens) endpoint * `tokenIdTo`: Destination token `_id` from [`/dexTokens`](/developer-hub/core-concepts/tokens-networks#fetch-dex-tokens) endpoint * `amount`: Amount to swap (float: `1` = 1 token) * `slippage` (optional): Slippage percentage (e.g., `0.5` for 0.5%) * `fromAddress` (optional): Specific source address for the swap * `toAddress` (optional): Specific destination address for receiving tokens Remember to use the `_id` field from DEX tokens, not the `id` field. Learn more about [token identifiers](/developer-hub/core-concepts/tokens-networks#token-identifiers). #### Quote Response The response is an **array of quote options** from different DEX aggregators, sorted by best rate (highest output first). Each quote object contains: | Field | Type | Description | | -------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------- | | `swap` | string | DEX identifier code used in subsequent API calls | | `swapName` | string | Human-readable DEX name (e.g., "CowSwap", "Uniswap") | | `quoteId` | string | Unique quote ID - pass this to `/dexExchange` | | `amountOut` | number | Expected output amount (human-readable) | | `amountOutUsd` | number | USD value of output amount | | `netAmountOut` | number | Net output after fees | | `feeUsd` | number | Fee amount in USD | | `duration` | number | Estimated swap duration in minutes | | `type` | string | Always `"dex"` for DEX swaps | | `logoUrl` | string | DEX logo image URL for UI display | | `raw` | object | Full route data - **pass this as `route` to subsequent endpoints** | | `supportsSignatures` | boolean | Whether this DEX supports gasless signatures | | `markupSupported` | boolean | Whether partner markup is supported | | `rewardsAvailable` | boolean | Whether rewards are available for this route | | `filtered` | boolean | If `true` and sender/receiver addresses differ on same-chain swaps, this route is not supported and should be disabled in UI | **Filtering Routes**: When `filtered: true`, the route does not support different sender and receiver addresses for same-chain swaps. You must either: * Filter out these routes from the UI when `addressFrom !== addressTo` * Disable the route option and show a message explaining it's not available for this configuration ### Step 3: Check Approvals and Signatures Before executing a swap, check what's needed from the user: ```javascript JavaScript theme={null} const approvalCheck = await fetch(`${API_BASE_URL}/dexApprove`, { method: 'POST', headers: { 'Authorization': `${API_KEY}:${API_SECRET}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ tokenIdFrom: '6689b73ec90e45f3b3e51566', tokenIdTo: '6689b73ec90e45f3b3e51553', amount: 1, addressFrom: '0x45CF73349a4895fabA18c0f51f06D79f0794898D', // user's address swap: selectedQuote.swap, // From step 2 route: selectedQuote.raw // From step 2 }) }); const { approvals, signatures } = await approvalCheck.json(); ``` ```bash cURL theme={null} curl -X POST "https://api-partner.houdiniswap.com/dexApprove" \ -H "Authorization: your_api_key:your_api_secret" \ -H "Content-Type: application/json" \ -d '{ "tokenIdFrom": "6689b73ec90e45f3b3e51566", "tokenIdTo": "6689b73ec90e45f3b3e51553", "amount": 1, "swap": "cs", "addressFrom": "0x45CF73349a4895fabA18c0f51f06D79f0794898D", "route": {...} }' ``` **Response Contains:** * `approvals`: Array of on-chain approval transactions (may be empty) * `signatures`: Array of signatures needed (may be empty) **Both Arrays Can Exist**: You may receive both approvals AND signatures. Handle approvals first, then signatures. #### Understanding Approvals If the `approvals` array is not empty, the user must approve the DEX to spend their tokens. See more in Step 4. #### Understanding Signatures The `signatures` array can contain two types: **1. SINGLE Type** - Simple one-time signature: **Action:** User signs once, add to results, done. **2. CHAINED Type** - Multi-step signature (currently only for Cowswap): **Action:** User signs, call `/chainSignatures`, repeat until `isComplete: true`. See more in Step 5. ### Step 4: Send Approval Transactions (if needed) If the `approvals` array is not empty, broadcast approval transactions: ```javascript JavaScript theme={null} // Send each approval transaction if (approvals && approvals.length > 0) { for (const approval of approvals) { // Send approval transaction through user's wallet const approvalTx = await walletProvider.sendTransaction({ to: approval.to, // Token contract address data: approval.data // Encoded approve() call }); // Wait for transaction to be mined await approvalTx.wait(); console.log('Approval confirmed:', approvalTx.hash); } } ``` ```bash Example theme={null} # User broadcasts approval transaction via wallet # Example: Approving USDC to be spent by Uniswap router to: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" # USDC token data: "0x095ea7b3..." # approve(spender, amount) encoded ``` **Skip This Step If**: No approvals were required (empty `approvals` array in Step 3). ### Step 5: Process Signatures (if needed) Handle signature requests from Step 3: ```javascript JavaScript theme={null} // Helper function to process all signatures async function processSignatures(signatures) { if (!signatures || signatures.length === 0) return []; const results = []; for (const sig of signatures) { // Prompt user to sign 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 CHAINED and not complete, get next signature if (sig.type === 'CHAINED' && !sig.isComplete) { const nextSigResponse = await fetch(`${API_BASE_URL}/chainSignatures`, { method: 'POST', headers: { 'Authorization': `${API_KEY}:${API_SECRET}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ tokenIdFrom, tokenIdTo, addressFrom: userWalletAddress, route: quote.route, swap: quote.swap, previousSignature: signatureObject, signatureKey: sig.key, signatureStep: sig.step }) }); const { chainSignatures } = await nextSigResponse.json(); // Recursively process next signature if (chainSignatures?.length > 0) { const chainResults = await processSignatures(chainSignatures); if (chainResults.length > 0) { results.push(chainResults[chainResults.length - 1]); // Only add final } } } else { // SINGLE type or final CHAINED signature results.push(signatureObject); } } return results; } // Usage const collectedSignatures = await processSignatures(signatures); ``` **Key Points:** * **SINGLE**: User signs once, done * **CHAINED**: User signs → API call → User signs again → Repeat until complete * Only keep the **final** signature from CHAINED sequences **Skip This Step If**: No signatures were required (empty `signatures` array in Step 3). ### Step 6: Check Allowance (if approvals were sent) If you sent approval transactions in Step 4, verify they are confirmed on-chain: ```javascript JavaScript theme={null} // Poll until approval is confirmed on-chain if (approvals && approvals.length > 0) { let hasAllowance = false; while (!hasAllowance) { const allowanceCheck = await fetchFromHoudini('/dexHasEnoughAllowance', { tokenIdFrom: '6689b73ec90e45f3b3e51566', tokenIdTo: '6689b73ec90e45f3b3e51553', amount: 1, addressFrom: "0x45CF73349a4895fabA18c0f51f06D79f0794898D", swap: quote.swap, route: quote.raw }); hasAllowance = allowanceCheck.hasEnoughAllowance; if (!hasAllowance) { await sleep(30000); // Wait 30 seconds before checking again } } console.log('Allowance confirmed!'); } ``` ```bash cURL theme={null} curl -X GET "https://api-partner.houdiniswap.com/dexHasEnoughAllowance?tokenIdFrom=6689b73ec90e45f3b3e51566&tokenIdTo=6689b73ec90e45f3b3e51553&amount=1&addressFrom=0x45CF73349a4895fabA18c0f51f06D79f0794898D&swap=cs&route=..." \ -H "Authorization: your_api_key:your_api_secret" ``` **Skip This Step If**: No approvals were sent in Step 4. ### Step 7: Execute the Swap Now execute the swap with any collected signatures: ```javascript JavaScript theme={null} const swapResponse = await fetch(`${API_BASE_URL}/dexExchange`, { method: 'POST', headers: { 'Authorization': `${API_KEY}:${API_SECRET}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ tokenIdFrom: '6689b73ec90e45f3b3e51566', tokenIdTo: '6689b73ec90e45f3b3e51553', amount: 26.2244, addressFrom: userWalletAddress, // User's wallet address addressTo: destination address, // Destination address to receive fund route: quote.raw, // Route object from quote response swap: quote.swap, // DEX identifier (e.g., "zx", "cs", "un") quoteId: quote.quoteId, // Quote ID from quote response signatures: collectedSignatures, // From Step 5 (can be empty array) destinationTag: '', // For chains that require memo/tag deviceInfo: 'web', // Device type: 'web', 'ios', 'android' isMobile: false, // Boolean indicating mobile device walletInfo: 'MetaMask', // Wallet name being used (e.g., "MetaMask", "Rabby Wallet") slippage: null // Custom slippage percentage (null = use default) 0.5 = 0.5% }) }); const { order } = await swapResponse.json(); console.log('Swap created:', order.houdiniId); console.log('Off-chain?', order.metadata.offChain); ``` ```bash cURL theme={null} curl -X POST "https://api-partner.houdiniswap.com/dexExchange" \ -H "Authorization: your_api_key:your_api_secret" \ -H "Content-Type: application/json" \ -d '{ "tokenIdFrom": "6689b73ec90e45f3b3e51566", "tokenIdTo": "6689b73ec90e45f3b3e51553", "amount": 26.2244, "addressFrom": "0xb7dE6b6eEBF7401aFea5a49D6405C9048fEf2d40", "addressTo": "0xb7dE6b6eEBF7401aFea5a49D6405C9048fEf2d40", "route": {...}, "swap": "zx", "quoteId": "69155e0bdb5ab0cbe27e2709", "signatures": [], "destinationTag": "", "deviceInfo": "web", "isMobile": false, "walletInfo": "MetaMask", "slippage": null }' ``` **Request Parameters:** * `tokenIdFrom`: Source token ID * `tokenIdTo`: Destination token ID * `amount`: Amount to swap (float) * `addressFrom`: User's wallet address (source) * `addressTo`: Destination address for receiving tokens * `route`: Complete route object from quote response (`quote.raw`) * `swap`: DEX identifier from quote (e.g., "zx" for 0x, "cs" for Cowswap) * `quoteId`: Quote ID from quote response * `signatures`: Array of signature objects from Step 5 (empty if none required) * `destinationTag`: Memo/tag for chains that require it (empty string if not needed) * `deviceInfo`: Device type - "web", "ios", "android", or custom identifier * `isMobile`: Boolean indicating if request is from mobile device * `walletInfo`: Name of wallet being used (e.g., "MetaMask", "Rabby Wallet") * `slippage`: Custom slippage tolerance (null to use default from quote) **Response Contains:** * `order.houdiniId`: Unique swap identifier for tracking * `order.metadata.offChain`: Boolean indicating if user transaction is needed * `order.metadata.to`: DEX router address (if offChain: false) * `order.metadata.data`: Encoded swap call (if offChain: false) * `order.metadata.value`: ETH value for native swaps (if offChain: false) #### Broadcast Transaction and Confirm After receiving the swap order, you need to: 1. Have the user broadcast the transaction (if `offChain: false`) 2. Call `/dexConfirmTx` to notify Houdini of the transaction hash User must broadcast the transaction, then confirm with Houdini: ```javascript theme={null} if (!order.metadata.offChain) { // Step 1: User sends transaction via wallet const tx = await walletProvider.sendTransaction({ to: order.metadata.to, data: order.metadata.data, value: order.metadata.value || '0' }); const receipt = await tx.wait(); const txHash = receipt.hash; console.log('Transaction broadcast:', txHash); // Step 2: Confirm with Houdini API await fetch(`${API_BASE_URL}/dexConfirmTx`, { method: 'POST', headers: { 'Authorization': `${API_KEY}:${API_SECRET}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ houdiniId: order.houdiniId, txHash: txHash }) }); console.log('Transaction confirmed with Houdini'); } ``` Backend handles execution (e.g., Cowswap), but you still need to confirm: ```javascript theme={null} if (order.metadata.offChain) { // No user transaction needed - Houdini backend will execute the swap // But still need to call dexConfirmTx to start processing await fetch(`${API_BASE_URL}/dexConfirmTx`, { method: 'POST', headers: { 'Authorization': `${API_KEY}:${API_SECRET}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ houdiniId: order.houdiniId, txHash: undefined // No transaction hash for off-chain swaps }) }); console.log('Off-chain swap confirmed with Houdini'); } ``` **Critical Step**: You MUST call `/dexConfirmTx` after creating the swap order: * **On-chain swaps**: Pass the transaction hash after user broadcasts * **Off-chain swaps**: Pass `txHash: undefined` to start backend processing Without this call, the swap will not be processed and status tracking will not work. ### Step 8: Track Swap Status Monitor the swap progress: ```javascript JavaScript theme={null} const status = await fetchFromHoudini('/status', { id: order.houdiniId }); console.log('Status:', status.status); console.log('Transaction hash:', status.txHash); ``` ```bash cURL theme={null} curl -X GET "https://api-partner.houdiniswap.com/status?id=xyz123" \ -H "Authorization: your_api_key:your_api_secret" ``` **Status Codes:** * `0` = WAITING - Awaiting transaction * `1` = CONFIRMING - Transaction submitted, waiting for confirmations * `2` = EXCHANGING - Processing swap * `4` = COMPLETED - Swap complete ✅ * `6` = FAILED - Swap failed ❌ **Polling**: Check status every 30 seconds for on-chain swaps. They typically complete in seconds to a few minutes depending on network congestion. ## Complete Example For a complete, runnable Node.js example: Complete DEX swap script with approval handling and wallet simulation ## Next Steps Learn about CEX-based private swaps Integrate fast single-hop CEX swaps Understand swap status progression Handle errors and edge cases # Private Swap Integration API v1 Source: https://docs.houdiniswap.com/api-v1/swap-flows/private-swap Complete guide to integrating multi-hop private swaps with maximum privacy Deprecation of the REST v1 swap endpoints begins on September 30, 2026. Please migrate to the Partner API v2. Dashboard and authentication endpoints remain on v1 GraphQL and are not affected. [Read the migration guide](https://docs.houdiniswap.com/migration/v1-to-v2) ## Overview Private swaps route through **multiple CEX hops** to provide maximum privacy. Like standard swaps, they use [CEX tokens](/developer-hub/core-concepts/tokens-networks#cex-tokens) from the `/tokens` endpoint. Optionally uses Monero (XMR) for enhanced anonymity. This breaks transaction trails across exchanges, providing enhanced anonymity without requiring wallet connections. **Best For**: Users who prioritize privacy and are willing to accept longer completion times (15-45 minutes) for enhanced anonymity. ## Key Features Routes through 2 exchanges to break transaction trail Can use Monero for untraceable intermediate transactions No browser wallet or approvals required No direct on-chain link between source and destination ## How It Works Private swaps follow this multi-hop flow: Fetch available tokens from `/tokens` Get a quote with `anonymous: true` to request private routing Submit swap with destination address Funds route through multiple CEXs (optionally via Monero) Final output sent to destination with broken trail ## Integration Guide ### Step 1: Get Supported Assets Before requesting a quote, fetch the available tokens. Learn more about [CEX tokens](/developer-hub/core-concepts/tokens-networks#cex-tokens). **Performance Note**: The `/tokens` endpoint can take several seconds to minute to respond due to the large token list. * Save tokens in your backend database * Load tokens on server startup or via scheduled job * Serve token list from your database to frontend * Refresh cache periodically (e.g., every 24 hours) ```javascript JavaScript theme={null} const tokens = await fetchFromHoudini('/tokens'); console.log('Available tokens:', tokens.length); ``` ```bash cURL theme={null} # Get CEX tokens curl -X GET "https://api-partner.houdiniswap.com/tokens" \ -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" ``` ### Step 2: Request Private Quote Request a quote with `anonymous: true` for maximum privacy. Use token symbols from the [`/tokens`](/developer-hub/core-concepts/tokens-networks#fetch-cex-tokens) endpoint: ```javascript JavaScript theme={null} // Request a private quote const quote = await fetchFromHoudini('/quote', { amount: '1', from: 'ETH', // Token symbol from /tokens to: 'SOL', // Token symbol from /tokens anonymous: 'true', // Enable private routing useXmr: 'false' // System decides XMR usage }); console.log('Route type:', quote.type); // "private" console.log('Privacy hops:', quote.path); // "se:cl" (multi-hop) console.log('XMR amount:', quote.xmrAmount); // Amount routed via Monero console.log('ETA:', quote.duration, 'minutes'); ``` ```bash cURL theme={null} curl -X GET "https://api-partner.houdiniswap.com/quote?amount=1&from=ETH&to=SOL&anonymous=true&useXmr=false" \ -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" ``` ### Quote Response ```json theme={null} { "amountOut": 23.6634, "amountIn": 1, "type": "private", "duration": 11, "path": "cl:qx", "xmrAmount": 38.231647970000004, "inQuoteId": "694cd4b5d924391f000561da", "outQuoteId": "694cd4b5d924391f000561e3", "quoteId": "694cd4b5d924391f000561e3", "amountOutUsd": 2893.323918, "rewardsAvailable": true } ``` **Key Private Route Fields:** * `type`: `"private"` indicating multi-hop routing * `path`: Multi-hop CEX path (e.g., `"cl:qx"` = Changelly → Quickex) * `xmrAmount`: Amount routed through Monero privacy layer * `inQuoteId` / `outQuoteId`: Separate quotes for each hop * `duration`: Estimated time to complete the exchange, longer completion time (15-45 min) due to multi-hop ### Step 3: Create Private Swap Create the swap order using the `/exchange` endpoint: ```javascript JavaScript theme={null} // Create a private swap const swapRequest = { amount: 1, from: 'ETH', to: 'SOL', addressTo: '1nc1nerator11111111111111111111111111111111', receiverTag: '', anonymous: true, // Enable private routing ip: '192.168.1.1', userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', timezone: 'America/New_York', useXmr: false }; const response = await fetch(`${API_BASE_URL}/exchange`, { method: 'POST', headers: { 'Authorization': `${API_KEY}:${API_SECRET}`, 'Content-Type': 'application/json', 'x-user-ip': swapRequest.ip, 'x-user-agent': swapRequest.userAgent, 'x-user-timezone': swapRequest.timezone }, body: JSON.stringify(swapRequest) }); const swap = await response.json(); console.log('Private swap created:', swap.houdiniId); console.log('Deposit to:', swap.senderAddress); ``` ```bash cURL theme={null} curl -X POST "https://api-partner.houdiniswap.com/exchange" \ -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 '{ "amount": 1, "from": "ETH", "to": "SOL", "addressTo": "1nc1nerator11111111111111111111111111111111", "receiverTag": "", "anonymous": true, "ip": "192.168.1.1", "userAgent": "Mozilla/5.0...", "timezone": "America/New_York", "useXmr": false }' ``` ### Exchange Response ```json theme={null} { "houdiniId": "bwc5iKVeeW5GiQpLHCm65w", "created": "2025-12-25T06:13:21.293Z", "senderAddress": "0xA2fC2BD472aB6FAF3176EBcBCaeeC7f95F563Ada", "receiverAddress": "1nc1nerator11111111111111111111111111111111", "anonymous": true, "status": 0, "inAmount": 1, "inSymbol": "ETH", "outAmount": 23.66258493, "outSymbol": "SOL", "eta": 28, "expires": "2025-12-25T06:43:21.293Z", "quote": { "path": "se:cl", "xmrAmount": 10393.30176114, "type": "private" }, "inStatus": 0, "outStatus": 0 } ``` **Critical**: Send exactly `inAmount` of `inSymbol` to the `senderAddress` within the expiration time (typically 30 minutes). ### Step 4: Monitor Swap Status Poll the status endpoint to track the multi-hop progress: ```javascript JavaScript theme={null} // Check swap status const status = await fetchFromHoudini('/status', { id: swap.houdiniId }); console.log('Overall status:', status.status); console.log('Input leg:', status.inStatus); // First hop status console.log('Output leg:', status.outStatus); // Second hop status ``` ```bash cURL theme={null} curl -X GET "https://api-partner.houdiniswap.com/status?id=bwc5iKVeeW5GiQpLHCm65w" \ -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" ``` ### Private Swap Status Progression Private swaps have additional status stages for multi-hop routing: ```text theme={null} 0 (WAITING) ↓ User sends deposit 1 (CONFIRMING) ↓ Deposit confirmed 2 (EXCHANGING) ↓ First CEX hop processing 3 (ANONYMIZING) ↓ Routing through privacy layer (XMR) ↓ Second CEX hop processing 4 (COMPLETED) ``` **Status Fields:** * `status`: Overall swap status (0-8) * `inStatus`: First hop status * `outStatus`: Second hop status (private swaps only) **Polling**: Check status every 30 seconds or more. Private swaps take 15-45 minutes due to multi-hop routing. ## Complete Example For a complete, runnable Node.js example: Complete private swap script with multi-hop status tracking ## Best Practices Clearly communicate the 15-45 minute completion time for private swaps. Users should understand they're trading speed for privacy. Explain how multi-hop routing provides privacy benefits. Help users understand what they're getting. Implement proper loading states and progress indicators. Private swaps take longer due to multiple hops. ## Common Issues **Cause**: Multi-hop routing through 2 exchanges takes longer **Solution**: This is normal for private swaps. Monitor `inStatus` and `outStatus` to see which hop is processing. **Question**: "How private is this really?" **Answer**: Private swaps break the transaction trail by routing through multiple exchanges. When XMR routing is used, it adds an untraceable intermediate step. However, this is not absolute anonymity - compliance checks still apply. ## Next Steps On-chain decentralized swaps Understand swap status progression Learn about private vs standard routing # Standard Swap Flow API v1 Source: https://docs.houdiniswap.com/api-v1/swap-flows/standard-swap Integrate standard swaps with single-hop CEX routing for fast execution Deprecation of the REST v1 swap endpoints begins on September 30, 2026. Please migrate to the Partner API v2. Dashboard and authentication endpoints remain on v1 GraphQL and are not affected. [Read the migration guide](https://docs.houdiniswap.com/migration/v1-to-v2) ## Overview Standard swaps use a single centralized exchange (CEX) for routing, providing the fastest execution times. These swaps use [CEX tokens](/developer-hub/core-concepts/tokens-networks#cex-tokens) from the `/tokens` endpoint and are routed directly through one exchange, completing in 3-30 minutes on average. **Best For**: Users who prioritize speed and want the fastest completion times (typically 3-30 minutes) with straightforward single-hop routing. ## How It Works Standard swaps follow this flow: Fetch available tokens from `/tokens` Get a quote with `anonymous: false` for standard routing Submit swap order with user's destination address Funds are routed through one centralized exchange Output tokens sent directly to destination address ## Integration Guide ### Step 1: Get Supported Assets Before requesting a quote, fetch the available tokens. Learn more about [CEX tokens](/developer-hub/core-concepts/tokens-networks#cex-tokens). **Performance Note**: The `/tokens` endpoint can take several seconds to minute to respond due to the large token list. * Save tokens in your backend database * Load tokens on server startup or via scheduled job * Serve token list from your database to frontend * Refresh cache periodically (e.g., every 24 hours) ```javascript JavaScript theme={null} const tokens = await fetchFromHoudini('/tokens'); console.log('Available tokens:', tokens.length); ``` ```bash cURL theme={null} # Get CEX tokens curl -X GET "https://api-partner.houdiniswap.com/tokens" \ -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" ``` ### Step 2: Request Quote Request a quote with `anonymous: false` for standard routing. Use token symbols from the [`/tokens`](/developer-hub/core-concepts/tokens-networks#fetch-cex-tokens) endpoint: ```javascript JavaScript theme={null} const quote = await fetchFromHoudini('/quote', { amount: '1', from: 'ETH', // Token symbol from /tokens to: 'USDC', // Token symbol from /tokens anonymous: 'false', // Standard routing useXmr: 'false' }); console.log('Quote type:', quote.type); // "standard" console.log('CEX provider:', quote.swapName); // e.g., "Changelly" console.log('ETA:', quote.duration, 'minutes'); ``` ```bash cURL theme={null} curl -X GET "https://api-partner.houdiniswap.com/quote?amount=1&from=ETH&to=USDC&anonymous=false&useXmr=false" \ -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" ``` ### Quote Response ```json theme={null} { "amountOut": 2456.78, "amountIn": 1, "min": 0.01021009, "max": 9007199254740991, "type": "standard", "duration": 3, "path": "cl", "swap": "cl", "swapName": "Changelly", "logoUrl": "https://api.houdiniswap.com/assets/logos/changelly.jpg", "quoteId": "694cd4ef6ca7023b5e00a288", "markupSupported": false, "amountOutUsd": 2916.1990075863, "rewardsAvailable": true } ``` **Key Fields:** * `type`: `"standard"` for single-hop routing * `swap`: CEX provider code (e.g., `"cl"` for Changelly) * `swapName`: Human-readable CEX name * `path`: Single CEX routing path * `duration`: Estimated completion time in minutes * `markupSupported`: Whether markup/fees can be added ### Step 3: Create Swap Create the swap order using the `/exchange` endpoint: ```javascript JavaScript theme={null} const swapRequest = { amount: 1, from: 'ETH', to: 'USDC', addressTo: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', receiverTag: '', anonymous: false, // Standard routing ip: '192.168.1.1', userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', timezone: 'America/New_York', useXmr: false }; const response = await fetch(`${API_BASE_URL}/exchange`, { method: 'POST', headers: { 'Authorization': `${API_KEY}:${API_SECRET}`, 'Content-Type': 'application/json', 'x-user-ip': swapRequest.ip, 'x-user-agent': swapRequest.userAgent, 'x-user-timezone': swapRequest.timezone }, body: JSON.stringify(swapRequest) }); const swap = await response.json(); console.log('Swap created:', swap.houdiniId); console.log('Deposit to:', swap.senderAddress); ``` ```bash cURL theme={null} curl -X POST "https://api-partner.houdiniswap.com/exchange" \ -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 '{ "amount": 1, "from": "ETH", "to": "USDC", "addressTo": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "receiverTag": "", "anonymous": false, "ip": "192.168.1.1", "userAgent": "Mozilla/5.0...", "timezone": "America/New_York", "useXmr": false }' ``` ### Exchange Response ```json theme={null} { "houdiniId": "iBQMRX3xvXrFMGQi71ogo9", "created": "2025-12-25T06:13:46.673Z", "senderAddress": "0x7364a0b6c55004427a4a7c26355ce9c75ef56194", "receiverAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "anonymous": false, "expires": "2025-12-25T06:43:46.673Z", "status": 0, "inAmount": 1, "inSymbol": "ETH", "outAmount": 23.85541415, "outSymbol": "USDC", "eta": 3, "inAmountUsd": 2937, "inCreated": "2025-12-25T06:13:46.673Z", "id": "694cd61a3544ef38231cc6f4", "quote": { "amountIn": 1, "amountOut": 23.85353526, "min": 0.01021398, "max": 9007199254740991, "path": "cl", "type": "standard" }, "outToken": { "id": "6689b73ec90e45f3b3e51558", "symbol": "USDC", "name": "USD Coin", "decimals": 6, "price": 1.0, "chain": "ethereum" }, "inToken": { "id": "6689b73ec90e45f3b3e51566", "symbol": "ETH", "name": "Ethereum", "decimals": 18, "price": 2937, "chain": "ethereum" }, "inStatus": 0 } ``` **Key Response Fields:** * `houdiniId`: Unique swap identifier - use this to check status * `senderAddress`: Deposit address where you send funds * `receiverAddress`: Your destination address * `inAmount` / `outAmount`: Expected amounts for the swap * `status`: Current swap status (`0` = pending deposit) * `inStatus`: Detailed status for the swap * `expires`: Quote expiration time (typically 30 minutes) * `eta`: Estimated time to completion in minutes * `quote.path`: Single CEX provider code **Important**: Send exactly `inAmount` of `inSymbol` to the `senderAddress` within the expiration time. ### Step 4: Send Deposit After creating the swap, send the tokens to the deposit address: Use `senderAddress` from the exchange response Send exactly `inAmount` of the source token Status will change from `0` (WAITING) to `1` (CONFIRMING) ### Step 5: Monitor Status Poll the status endpoint to track swap progress: ```javascript JavaScript theme={null} const status = await fetchFromHoudini('/status', { id: swap.houdiniId }); console.log('Status:', status.status); // 0 = WAITING (awaiting deposit) // 1 = CONFIRMING (deposit received) // 2 = EXCHANGING (processing on CEX) // 4 = COMPLETED (swap complete) ``` ```bash cURL theme={null} curl -X GET "https://api-partner.houdiniswap.com/status?id=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" ``` ### Status Progression Standard swaps follow this status flow: ```text theme={null} 0 (WAITING) ↓ User sends deposit 1 (CONFIRMING) ↓ Deposit confirmed 2 (EXCHANGING) ↓ CEX processes swap 4 (COMPLETED) ``` **Polling**: Check status every 30 seconds. Standard swaps typically complete in 3-30 minutes. ## Complete Example For a complete, runnable Node.js example: Complete standard swap script with status monitoring ## Best Practices * Poll status every 30 seconds * Handle all status codes properly (0-8) * Store `houdiniId` for future reference * Check for quote expiration before creating swap * Validate deposit address format * Handle network congestion delays * Implement retry logic for API calls * Check final states: COMPLETED (4), FAILED (6), EXPIRED (5), REFUNDED (7) * Show clear deposit instructions * Display countdown for quote expiration * Provide CEX provider information * Show estimated completion time * Display transaction progress clearly * Validate all addresses before submitting * Never expose API keys in frontend * Verify compliance headers are correct * Store swap records for audit trail * Use backend-only API integration ## Common Issues **Causes**: * Network congestion on source/destination chain * CEX processing delays * Deposit confirmation delays **Solution**: Continue monitoring. Most swaps complete within 2x the estimated time. **Issue**: User sent incorrect amount to deposit address **Solution**: * Partial refund may be processed * Contact support with `houdiniId` * Always send exact `inAmount` **Cause**: Took too long to deposit after creating swap **Solution**: Create new swap with fresh quote. Quotes expire after 30 minutes. **Issue**: Swap stuck at status 1 (CONFIRMING) **Solution**: * Wait for blockchain confirmations * Check transaction on block explorer * Verify correct amount was sent ## Next Steps Learn about private multi-hop swaps Integrate on-chain DEX swaps Understand swap status progression Handle errors and edge cases # Release notes Source: https://docs.houdiniswap.com/changelog/release-notes Stay up to date with Houdini Swap release notes, including new integrations, swap improvements, API updates, security enhancements and platform fixes. **New Integrations** * The Robinhood chain is now supported natively, so its network and tokens are recognized across the platform. * PactSwap native asset coverage now extends to newly added PactSwap networks, including Arbitrum. **Routing & Pricing** * Route execution venue is now shown on aggregator routes, so you can see which exchange fills your trade. * Private swap quoting now keeps the full set of floating routes visible alongside fixed rates, so you see every available option when comparing. * Minimum and maximum limits now reflect the largest amount the platform can actually fill, so large trades are quoted against real ceilings. * Fixed-rate quotes now account for the destination network fee, so the quoted output matches what is delivered. * USD ceilings for fixed-rate swaps now apply consistently to both standard and private routes. * Multiswap now draws its per-transaction maximum from live limits, so validation matches what is applied at execution. **Swap Improvements** * Cross-chain DEX swaps now confirm the destination-chain fill before an order is marked complete, including routes to chains without a numeric chain ID. * ChainFlip swaps now record and display the payout transaction hash on completed orders. * Refunds are now routed through a different provider than the outbound swap, and refunds on memo-based chains carry the required tag. * Refund address handling and validation messaging have been fixed across affected routes, including Near Intents. * Pasting a contract address for a token without DEX liquidity now shows the token as selectable with its available routes, instead of a not-found state. **UI & Experience** * Redesigned fixed-rate and self-custody wallet messaging for clearer guidance at the point of decision. * Promotional banners now stay dismissed once their call-to-action is used. * An OFAC advisory notice is now shown in the swap flow. * Swap interface rendering has been refined for smoother transitions between quote states. **API** * Partner minimum and maximum responses now report the true executable ceiling per pair, and same-token pairs are rejected at validation. * Partner API documentation now renders correctly on mobile, with improved asset caching. **Security** * Various security and compliance improvements across the platform. **New Feature: EVM Batch Swap** * You can now bundle multiple swaps into a single transaction from an EVM wallet. Combine several trades into one signature and one on-chain deposit, then track every leg from one order view. Bundles handle expired, failed, or refunded legs gracefully, with automatic retries and fund recovery when a leg can't complete. **Routing & Pricing** * Uniswap now supports cross-chain swaps, expanding available routes for cross-chain pairs. * Private swaps now offer a fixed-rate option on the deposit leg, locking your entry rate for faster, more predictable private swaps. * Routing reliability has improved for private and multi-exchange swaps, with a fallback path when a preferred route is unavailable, so more swaps complete on the first attempt. * Quote handling has been refined so users comparing routes across multiple tabs can request quotes more freely before hitting rate limits. **Swap Improvements** * Refunded orders now link the refund transaction to the correct source-chain block explorer. * Private Solana-to-Solana swaps now complete reliably when a valid route is available. * Error responses now return distinct codes, so integrations can reliably tell different errors apart. **UI & Experience** * The order status view now shows accurate step progress and timing, including when a swap's deposit window expires. **API** * Partner API v2 now validates chain and token parameters more consistently, returning clearer responses for invalid input. * The GraphQL `productType` argument is now a typed enum, giving integrations discoverable, validated values across the exchange, multiExchange, and dexExchange mutations. / **New Integrations** * Robinhood-network wallets can now be connected through WalletConnect. **Routing & Pricing** * Wormhole quotes for stablecoin routes now return consistently and stay available across supported pairs. * Wormhole quote retrieval has been optimized so its routes surface reliably in the quote list. * Private swap amount limits now account for the full route before a quote is requested, giving users clearer bounds before they start. * ChainFlip quote and status handling has been updated to support the latest ChainFlip asset catalog. * Exact-out route cards now show the required input amount in the route list and handle quote refresh cleanly in the swap flow. **Swap Improvements** * DEX route cards now show a specific reason when a route is unavailable, distinguishing price-impact filtering from recipient-wallet restrictions. * Private swaps now show an accurate "no route available" result when an intermediary route can't be built, instead of an amount-bounds message. * Solana recipient validation now accepts valid wallets, smart-wallets, and multisig addresses, and blocks malformed Solana-looking addresses before submission. * Sonic DEX swaps now confirm on-chain deposits reliably through an upgraded network connection. * Stellar deposit handling now recognizes failed transactions so affected orders resolve correctly. * Multi-order recovery has been hardened so interrupted order setup can be safely retried. * Expired orders with real deposit evidence are now flagged for follow-up instead of remaining plain expired. **UI & Experience** * Users can now access a Send flow for private transfers directly from the swap interface, also available in widget mode for integrators. * Shared swap links with an unrecognized token now display clear feedback instead of silently loading an empty selector. * The HoudiniSwap blog has moved under houdiniswap.com/blog as part of the main-domain content migration. **API** * Partner API v2 now exposes a public status endpoint so integrators can check system availability before sending quote or exchange requests. * Partner API v2 quote and chain lookups now return clearer, validated responses for edge-case inputs. **Security** * Various security and compliance improvements across the platform. **New Integrations** * Near Intents is now supported as a DEX route, expanding cross-chain swap coverage through Near's intent-based settlement. * Added support for the Tempo network across the swap aggregator. **Routing & Pricing** * DEX quoting reliability improved across Wormhole and ChainFlip routes, so unsupported or unmapped token pairs now resolve cleanly instead of surfacing failed routes. * The aggregator now drops zero-output and non-viable routes, so only executable quotes appear as selectable options. * Fixed-rate quotes on smaller amounts are now validated against provider limits before being offered. * Estimated completion times for centralized-exchange swaps now use the provider's live estimate as the primary source, with private swaps combining both legs for a more accurate total. **Swap Improvements** * Tron DEX swaps that require a token approval now complete the approval step correctly across supported wallets. * Wormhole cross-chain transfers now execute with a higher gas allowance to prevent transactions from stalling. * Orders that stay in a transient state beyond the standard tracking window are now picked up by a dedicated recovery process that drives them to a final status. * Late deposits made after an order's window are now reconciled automatically once the swap completes, with the displayed output settling to the final confirmed amount. * Improved finalization and status accuracy so completed swap legs are recorded and reconciled consistently. **Multiswap** * Multiswap order creation via the Partner API now accepts per-order routing filters as a dedicated parameter, matching the web app. * The orders view now handles multiswap orders natively for clearer multi-leg tracking. * Added EVM bundle support for multiswap, including retry handling for full or partial order sets. **UI & Experience** * Token search now supports the full Sui token address format, so pasting a complete address returns the expected result. * The token search field stays fully scrollable and editable, keeping long addresses visible alongside the network selector. * Streamlined the order page by removing the bottom feedback survey. * Refined token search ranking so deprecated tokens no longer outrank common matches. **API** * Partner API v2 quote, token, and chain endpoints now return clearer, properly-typed validation responses for invalid or out-of-range inputs, including disabled-chain handling and consistent amount-bounds messaging. * The first exchange response now includes the deposit address directly, so partners no longer need a follow-up status poll to retrieve it. **Security** * Various security and compliance improvements across the platform. **Routing & Pricing** * Exchange creation now aligns submitted amounts with each asset’s supported blockchain precision before submitting transactions. * PactSwap integration now available **Swap Improvements** * Bitcoin Taproot DEX swaps now support Taproot accounts through the swap flow. * Wallet balances in the swap form refresh during active sessions and after users return from order details. * Refund address entry now includes a paste action for faster address input. * MegaETH network selection now switches the From token field to the native MegaETH asset. **UI & Experience** * Token search now prioritizes verified assets, while unverified tokens remain accessible through exact contract-address lookup with warning indicators. * The analytics page has been redesigned with a cleaner stats layout, volume chart placement, latest-swaps section, and responsive card behavior. * The browser extension download page is available from production. **API** * Partners can now filter /v2/chains by chain kind with case-insensitive matching and safer handling of invalid filter values. * /v2/chains documentation now points to the exact kind values returned by the API. * Order schemas now document runtime order fields including displayStatus, depositAddress, and deposit memo fields. * API v2 responses now align body-parse errors and account metadata fields with the published schema. * METIS transaction explorer links returned through the Partner API now resolve to transaction pages. **Security** Various security and compliance improvements across the platform. **New Integrations** * TRUMP on Solana is now available via private swap routes, enabling privacy-preserving swaps for the token on the Solana network. **Routing & Pricing** * Swap status progression is now strictly forward-moving — an issue causing the status to revert from "Swapping" back to "Waiting for deposit" mid-swap has been resolved. **Swap Improvements** * Order status display on the tracking page is now driven by a unified backend `displayStatus` field, eliminating edge cases where expired or in-progress orders could show conflicting states simultaneously. * SUI DEX swaps using the Max button now reserve sufficient gas for all wallet configurations, preventing `InsufficientCoinBalance` failures at execution time. * Amount-out-of-bounds errors on private swaps are handled more precisely, reducing false rejections on valid swap amounts near provider boundaries. **Multiswap** * The Multiswap Custom mode now shows inline validation warnings on recipient rows where an amount has been entered and then cleared, preventing partial batches from being submitted unintentionally. * Per-transaction amounts in the Multiswap creation summary now display at full precision, matching the values shown on the order tracking page. **UI & Experience** * DEX swaps now require users to confirm the recipient address before the approve and swap transactions are sent to the wallet — a confirmation step that displays the destination address and requires explicit acknowledgment, reducing the risk of funds being sent to custodial or exchange wallets. * Stellar (XLM) swaps on the order details page now support sending directly from a connected wallet, with native and token transfers both supported. * The swap button is now disabled when no route is selected, preventing a no-route error from being triggered by an accidental click. **API** * Partners can now create and manage multiple API keys per account, enabling key rotation workflows and separate credentials for different integrations. * Private swap quote and exchange endpoints on Partner API v2 now accept optional per-leg provider include/exclude filters, allowing independent provider selection for the in-leg and out-leg of a private swap. * Partner API v2 now returns a proper validation error (instead of a 500) when non-numeric or unicode characters are passed as amount values. **Security** * Various security and compliance improvements across the platform. **Routing & Pricing** * CowSwap routing on Ethereum has been corrected so native ETH pairs route through the proper path. * Quotes on low-liquidity BSC pairs are filtered to remove unusable routes before users see them. * Swap completion amounts are now recorded from provider-confirmed values, so the final receipt reflects the actual payout rather than the pre-swap estimate. * Route filters apply correctly from the moment quotes start loading — user-selected provider preferences are honored during the initial render. **Swap Improvements** * Tron DEX swaps now wait for on-chain confirmation before reporting a transaction hash, eliminating premature `pending` states. * Solana wallet deep links ("Confirm on Wallet" / "Open in Wallet") now work reliably across mobile and desktop. * Swap status progresses through to completion in cases where the transaction hash was previously not surfaced. * The "Connect Wallet" button now responds across all network configurations. * Resolved a Next.js deployment issue that caused brief page load errors immediately after new releases. **Multiswap** * All legs in a Multiswap order now share the same min/max bounds, keeping displayed limits and accepted values in sync. * UI polish: capped-height transaction summary panel, row deletion on hover, clearer disabled-button states, same-token validation messaging, and a cleaner recipient address display. * Per-transaction minimum amounts are now consistent between Multiswap, payment links, and the standard swap flow. **UI & Experience** * Redesigned Advanced Settings panel for a cleaner, more intuitive layout. * The "View Order" button in the active orders popover is now fully visible on smaller screens. * URL amount parameters using scientific notation (e.g. `?amount=1e5`) are now properly sanitized. **API** * Partner API v2 validation has been hardened — invalid inputs return proper validation errors, unknown query parameters are rejected, and token fields return consistent types. * The single-token lookup endpoint (`GET /tokens/{id}`) is now fully accessible. * Removed an upper-bound limit on quote request amounts. **Security** * Various security and compliance improvements across the platform. **New Integrations** * TON and TRON are now natively supported in the wallet connection kit — connect and swap directly without extra installs. * CowSwap DEX routes are now live, expanding DEX coverage. **Routing & Pricing** * Live price updates across all token feeds — quotes reflect current market data. * Quote subscriptions now refresh instantly when switching pairs or amounts, eliminating stale data between selections. * Quote integrity safeguards filter out extreme price outliers before they reach the UI. * Refreshed quotes now display the correct estimated output amount before confirmation. **Swap Improvements** * Solana address checksum validation catches invalid recipient addresses before order creation. * Minimum-amount guidance now shows the exact required minimum instead of a generic message when an amount is too low. **Multiswap** * Multiswap is available in Partner API v2 — partners can create, track, and batch-execute multi-leg swaps, including Solana batch transactions. **UI & Experience** * Faster app load — Stellar SDK and related modules now lazy-load, giving users on other chains a snappier startup. * The blog has moved to its own domain to improve main-app performance. * The swap button stays disabled until a recipient address is provided, preventing accidental submissions on mobile. **API** * Partner API v2 now exposes full account access programmatically — statistics, commission history, withdrawals, and profile data mirror the dashboard. * Every Partner API v2 error response now includes a `requestId` for easier support correlation. * The `/quotes` endpoint has broader country coverage and improved edge-case handling. **Security** * Ongoing security and compliance hardening across the platform. New Integrations * Ne verified partner is now live as a CEX exchange provider, expanding the pool of available routes for users. Routing & Pricing * Smarter swap retry logic — when a route fails, the system now retries other routes without abandoning the entire provider, resulting in significantly higher swap success rates on complex routes. * Wormhole bridge fees corrected — users swapping USDC cross-chain via Wormhole now see accurate, reasonable bridge fee estimates. * Cetus DEX (SUI) swap reliability restored — a quote calculation issue causing failed SUI-based swaps has been resolved. Swap Improvements * Clearer error messages — when a swap route fails, users now see actionable guidance ("Select another route if available, or try again later") instead of a dead-end technical message. * Order tracking page improvements — the order status page now shows a proper error state when an order is not found * "Connect Wallet" button restored on the order details page for EVM swaps — a chain-matching bug introduced by a new network addition had hidden the button for all EVM orders. * HyperEVM (SushiSwap) DEX swaps fixed — gas estimation failures on HyperEVM routes are resolved; swaps now complete successfully. UI & Experience * Deposit QR codes now support EIP-681 Payment URIs — scanning the QR code with a compatible wallet (Trust Wallet, MetaMask Mobile, etc.) auto-fills the deposit amount and address across all supported chains. * Swap confirmation screen updated — swap details (output amount, destination address) are now shown prominently and expanded by default; chain logos appear next to token tickers for at-a-glance chain identification. * Route sort tabs highlighted — the Best Rate / Fastest sort tabs now have a visual highlight to guide users toward the most relevant option; the Multiswap "New" badge has been retired. API * Partner API v2 significantly hardened — multiple response correctness fixes: proper error codes for disabled/missing tokens, USD output amounts, and a new unified `displayStatus` field that abstracts internal swap leg complexity into a single human-readable status string. Security * Various security and compliance improvements across the platform. New Integrations * Bittensor Native network is now live — users can swap to and from TAO on the native Bittensor chain alongside the existing EVM variant. Routing & Pricing * Improved Jupiter and Raydium ETA logic, due to almost instant swaps. * Dynamic min/max amount bounds now display correctly, preventing users from seeing stale or incorrect swap limits. Swap Improvements * DEX swaps with USDT no longer get stuck on "Approving" — the system now correctly handles USDT's non-standard allowance reset requirement before setting a new approval. * ChainFlip order status no longer incorrectly reverts from "Swapping" back to "Waiting for deposit" mid-swap; status progression is now strictly forward-moving. * TRON DEX swaps now check wallet balance before proceeding and surface a clear "insufficient balance" message instead of silently failing. UI & Experience * Users can now filter swap routes directly from the quote screen, making it easier to find preferred providers or route type. * Updated footer links due to new documentation rollout. Security * Various security and compliance improvements across the platform. New Integrations * Stellar (XLM) swaps are now available via the Sodax DEX provider, enabling cross-chain routes between Stellar and other supported networks. * Tron wallets can now be connected via WalletConnect, expanding Tron wallet support beyond TronLink. Routing & Pricing * Min/max swap limits are now dynamically sourced from providers — users see accurate per-route boundaries instead of generic platform-wide caps. * Token search results are now ranked by chain-level volume, surfacing the most relevant tokens first. Swap Improvements * Order status tracking is now more accurate — certain provider verification states that previously displayed as "Failed" now correctly show as in-progress. * DEX swap reliability improved across Wormhole (Solana), ChainFlip, and 0x Protocol routes, fixing approval handling and decimal precision issues. * On-chain transaction confirmation is now detected in real time via WebSocket watchers, delivering faster status updates. * Bitcoin address validation now enforces exact Bech32 length requirements, preventing truncated addresses from passing validation. Multiswap * Multiswap orders with uneven recipient amounts no longer fail during creation. UI & Experience * A new "Active Orders" module in the header lets users save, track, and quickly return to in-progress orders. * Wallet token balances (including BTC and SUI) now load from the backend, improving speed and accuracy across all supported chains. * Error messages on failed DEX routes now clearly guide users to select an alternative route instead of showing technical details. * Frontend performance improved through memory leak fixes, token list virtualization, and build optimizations. API * API v2 `/quote` and `/exchange` endpoints are now live, with updated developer documentation and a v1 → v2 migration guide. * Partners can now receive order status updates via webhooks in addition to WebSocket subscriptions. Security * Various security and compliance improvements across the platform. Routing & Pricing * Private swaps now check all available routing paths at once and automatically fall back if the first choice is unavailable — fewer failed quotes, smoother experience. * Integrated live price feeds from Pyth oracle for more accurate real-time token pricing on the swap page. Swap Improvements * Fixed an issue where a provider that couldn't handle the requested order size could still appear as the best quote — the system now filters out providers that can't fulfill the amount before showing rates. * Fixed a bug where DEX token approvals could get stuck without any feedback, leaving the swap in a loading state with no indication of what went wrong. * Improved error messages when a DEX swap is reverted — a clear explanation is now shown instead of a generic blockchain error. * Quote auto-refresh now pauses after 30 minutes of inactivity, so idle tabs no longer trigger unnecessary rate-limit errors. UI & Experience * Added a tooltip explaining how price deviation protection works — users can now hover to understand what happens if the rate moves more than 5% from the original quote. * Smarter recipient address validation — if a token contract address is accidentally pasted as a recipient, a clear warning is shown before submission. This now also applies to Payment Links and Multiswap. * Faster page loads — reduced initial bundle size by \~30kb with lazy-loaded animations. * Improved accessibility for screen readers on the research disclaimer modal. * Updated Zcash address validation — only transparent (`t1`) addresses are now accepted, preventing failed deliveries to unsupported address formats. API * Launched API v2 with new REST endpoints for quotes, swaps, token lists, chain info, and real-time order status via WebSocket — plus a full migration guide for partners upgrading from v1. * Completed a full Developer Hub documentation overhaul — fixed broken links, improved endpoint docs, and resolved integration blockers reported by partners. * Token balance lookups now run server-side for faster, more reliable balance display when a wallet is connected. Security * Various security hardening measures applied across the platform infrastructure. New Integrations * Integrated Bungee as a new cross-chain swap provider, adding more routes between EVM and Solana chains. * Integrated Allbridge as a new cross-chain bridge provider. * Added Stellar wallet support — Freighter and xBull wallets can now connect directly, with automatic trustline setup for Stellar assets. Swap Improvements * Fixed an issue where SushiSwap swaps could fail due to token approvals not being processed correctly. * Fixed a bug where PancakeSwap could show broken quotes as selectable routes. * Reduced occurrences of "no available paths" errors on private swaps by improving how the system validates routing availability. * Improved estimated completion times shown for DEX swaps to be more accurate across providers. Multiswap * Fixed Solana address input in Multiswap Custom mode — certain valid addresses starting with numbers were incorrectly rejected. * Fixed a rounding issue where amounts slightly below the minimum could slip through validation in Multiswap. UI & Experience * Updated the post-swap survey to ask which platforms users would like to see HoudiniSwap on next (browser extension, mobile app, in-wallet, etc.). * Users in restricted regions now see a clear explanation page instead of a confusing error code. * Fixed blog pages occasionally failing to load. API * Partners can now receive the deposit transaction hash in the status response for better order tracking. * Added configurable rate limits per partner for high-volume integrations. Security * Various security and compliance improvements across the platform. * Upgraded our core servers and DevOps architecture to sustain higher traffic loads while maintaining lower latency. * Refactored order creation logic to trigger *before* fetching deposit addresses, resulting in a much more responsive user experience. * Added support for the FOGO network. * Added Tron network directly via MetaMask connection. * Updated 0x swap to support transactions sent to alternative recipient addresses. * Fixed Jupiter "Ultra" integration for same-recipient transactions. * Improved status polling and error handling for Mayan and deBridge orders. * Added a validation to block users from accidentally entering "Token Addresses" as recipient addresses * New UI alerts now trigger if a rate fluctuates by more than 5% from the initial quote. * Fixed logic errors when switching between tokens that do and do not require Memos. * Added token balances directly within the search modal when your wallet is connected. * Replaced ambiguous error codes for hardware wallet (Ledger) connection issues. * Integrated a swap feedback form on the order page to collect direct community insights. # Order Lifecycle Source: https://docs.houdiniswap.com/developer-hub/core-concepts/order-lifecycle Understanding order states, status transitions, and lifecycle management ## What is an Order? An **order** represents a single swap transaction in Houdini's system. Each order: * Has a unique `houdiniId` for tracking * Progresses through numeric status codes (0-8) * Contains swap details (tokens, amounts, addresses) * Tracks execution progress and completion ## Status Code Overview Houdini API returns numeric status codes to represent the current state of an order: ```javascript theme={null} const ORDER_STATUS = { WAITING: 0, // Waiting for deposit CONFIRMING: 1, // Deposit being confirmed EXCHANGING: 2, // Swap executing ANONYMIZING: 3, // Private swap routing (private swaps only) COMPLETED: 4, // Swap completed successfully EXPIRED: 5, // Order expired before deposit FAILED: 6, // Swap failed REFUNDED: 7, // Funds refunded to user DELETED: 8 // Order cancelled/deleted }; ``` ## Order Status Flow ### Standard Swap Flow Fixed rate swaps follow the same status flow as standard swaps. The only difference is the `refundAddress` field — if the swap fails, the order moves to `REFUNDED` and funds may be returned there, depending on the provider. ```mermaid theme={null} stateDiagram-v2 [*] --> 0_WAITING 0_WAITING --> 1_CONFIRMING: Deposit received 0_WAITING --> 5_EXPIRED: Time expired 1_CONFIRMING --> 2_EXCHANGING: Deposit confirmed 2_EXCHANGING --> 4_COMPLETED: Swap successful 2_EXCHANGING --> 6_FAILED: Swap error 6_FAILED --> 7_REFUNDED: Refund processed 6_FAILED --> 8_DELETED: Order cancelled 4_COMPLETED --> [*] 5_EXPIRED --> [*] 7_REFUNDED --> [*] 8_DELETED --> [*] ``` ### Private Swap Flow Private swaps include an additional `ANONYMIZING` (3) status for multi-hop routing: ```mermaid theme={null} stateDiagram-v2 [*] --> 0_WAITING 0_WAITING --> 1_CONFIRMING: Deposit received 0_WAITING --> 5_EXPIRED: Time expired 1_CONFIRMING --> 2_EXCHANGING: Deposit confirmed 2_EXCHANGING --> 3_ANONYMIZING: First hop complete 3_ANONYMIZING --> 4_COMPLETED: Privacy routing complete 3_ANONYMIZING --> 6_FAILED: Routing error 6_FAILED --> 7_REFUNDED: Refund processed 4_COMPLETED --> [*] 5_EXPIRED --> [*] 7_REFUNDED --> [*] ``` ## Status Definitions ### Active States **Status Code**: `0` **Description**: Order created, waiting for user to send deposit. **What's Happening**: * Deposit address generated and active * Monitoring blockchain for incoming transaction * Quote validity timer counting down * Order will expire if no deposit received in time **Integrator Action**: * **Display deposit address prominently** * Show exact amount to send * Show correct network/chain * Display expiration countdown * Optionally show QR code for easy deposit **Typical Duration**: User-dependent (0-30 minutes) **Next States**: `1` (CONFIRMING), `5` (EXPIRED) **Status Code**: `1` **Description**: Deposit received, waiting for blockchain confirmations. **What's Happening**: * Deposit transaction detected on-chain * Waiting for required block confirmations * Once confirmed, swap will begin execution **Integrator Action**: * Update UI to show deposit detected * Display "Confirming deposit..." message * Show confirmation progress if available * Continue polling status **Typical Duration**: 30 seconds - 5 minutes (chain-dependent) **Next States**: `2` (EXCHANGING) **Status Code**: `2` **Description**: Deposit confirmed, swap actively executing. **What's Happening**: * **DEX Routes**: On-chain swap transaction being confirmed * **Standard Swaps**: Single exchange processing * **Private Swaps**: First hop of multi-hop route executing **Integrator Action**: * Show active progress indicator * Poll status regularly **Typical Duration**: * Standard: 3-30 minutes * Private: Moves to status `3` (ANONYMIZING) * DEX: 30 seconds - 15 minutes **Next States**: `3` (ANONYMIZING - private only), `4` (COMPLETED), `6` (FAILED) **Status Code**: `3` **Description**: Private swap routing through privacy layer (private swaps only). **What's Happening**: * First exchange hop completed * Routing through privacy layer (potentially via Monero) * Second exchange hop executing * Breaking transaction trail across multiple venues **Integrator Action**: * Show multi-hop progress indicator * Continue polling **Typical Duration**: 10-40 minutes (multi-hop routing) **Next States**: `4` (COMPLETED), `6` (FAILED) **Only Appears In**: Private swaps with `anonymous: true` Learn more about private swaps in the [Private Swap Integration Guide](/developer-hub/swap-flows/private-swap). ### Terminal States **Status Code**: `4` **Description**: Swap successfully completed, funds delivered to destination. **What's Happening**: * Destination tokens sent to user's `addressTo` * Transaction confirmed on destination chain * Order fully settled and complete **Integrator Action**: * **Display success message prominently** * Show final amount received * Provide destination transaction hash * Link to blockchain explorer * **Stop status polling** **This State is Final**: ✅ Yes **Status Code**: `5` **Description**: Order expired before deposit was received. **What's Happening**: * Quote validity window passed (typically 30 minutes) * Deposit address deactivated * No deposit transaction detected * Order automatically expired by system **Integrator Action**: * Inform user order expired * Explain they need to create a new order for a fresh quote * **Important**: If user claims they sent funds to expired address, escalate to support immediately * **Stop status polling** **This State is Final**: ✅ Yes **Common Cause**: User took too long to send deposit **Status Code**: `6` **Description**: Swap encountered an error and could not complete. **What's Happening**: * CEX unavailable, insufficient liquidity, or technical error * Deposit was received but swap execution failed * System determining if refund is possible * Error details available in response **Integrator Action**: * Display error message from `message` field * Show refund status if available * Provide customer support contact **This State is Final**: ✅ Yes **Status Code**: `7` **Description**: Swap failed and original funds returned to user. **What's Happening**: * Swap failed; system attempting to return original deposit * Sent to `refundAddress` if provided when creating the order * May be partial refund if network fees deducted * **Note**: Refund behavior depends on the provider — some providers automatically return funds, others require the user to contact customer support **Integrator Action**: * Display refund status * Provide support contact for questions * **Stop status polling** **This State is Final**: ✅ Yes **Status Code**: `8` **Description**: Order was automatically deleted from the system. **What's Happening**: * Order was automatically removed after a retention period * This is a normal cleanup process for old orders * No issues or problems with the order - it completed its lifecycle * Historical data cleanup to maintain system performance **Integrator Action**: * Inform user that order data is no longer available * This typically occurs for orders that are several hours old * **Stop status polling** **This State is Final**: ✅ Yes ## InStatus and OutStatus Tracking (Optional) All swap types (Standard, DEX, and Private) provide additional status fields to track detailed progress: ### Status Fields * **`status`**: Overall order status (0-8) - applies to all swap types * **`inStatus`**: Input leg status - tracks the progress of receiving and processing the deposit * **`outStatus`**: Output leg status - only used in **private swaps** to track the second hop ### Standard and DEX Swaps (Single Hop) For standard CEX swaps and DEX swaps, only `inStatus` is used to track progress: ```javascript theme={null} const SWAP_STATUS_STANDARD_AND_DEX = { IN_STATUS_1: 1, // Waiting for deposit IN_STATUS_2: 2, // Deposit detected on-chain IN_STATUS_3: 3, // Swapping (executing trade) IN_STATUS_4: 4, // Sending output to destination IN_STATUS_5: 5 // Completed (swap finished) }; ``` **Example - Standard Swap Progress**: ```json theme={null} { "houdiniId": "abc123", "status": 2, // EXCHANGING "inStatus": 3, // Currently swapping } ``` ### Private Swaps (Multi-Hop) Private swaps use **both** `inStatus` and `outStatus` to track each hop separately: ```javascript theme={null} const SWAP_STATUS_PRIVATE = { // First hop (input leg) IN_STATUS_1: 1, // Waiting for deposit IN_STATUS_2: 2, // Deposit detected on-chain IN_STATUS_3: 3, // Swapping on first exchange IN_STATUS_4: 4, // Sending to privacy layer/second exchange // Second hop (output leg) - only for private swaps OUT_STATUS_2: 2, // Exchange - receiving from first hop OUT_STATUS_3: 3, // Swapping on second exchange OUT_STATUS_4: 4, // Sending final output to destination OUT_STATUS_5: 5 // Completed - final delivery done }; ``` **Example - Private Swap in Progress**: ```json theme={null} { "houdiniId": "bwc5iKVeeW5GiQpLHCm65w", "status": 3, // ANONYMIZING (overall status) "inStatus": 4, // First hop: sending to second exchange "outStatus": 2, // Second hop: receiving from first hop } ``` **Key Difference**: Standard and DEX swaps only use `inStatus` (single hop), while private swaps use both `inStatus` and `outStatus` (multi-hop) to track each exchange leg separately. ## Next Steps Complete integration guide for private swaps Fast single-hop CEX swap integration On-chain decentralized swap integration Detailed status endpoint documentation # Partner Stats Source: https://docs.houdiniswap.com/developer-hub/core-concepts/partner-stats Query your partner volume, weekly breakdowns, and time-series chart data # Partner Stats The stats endpoints return metrics scoped to your partner account — not platform-wide totals. Use them to build dashboards, monitor performance, or integrate your volume data into internal reporting. All stats endpoints require full API key authentication (`Authorization: :`). The public `partner-id` header does not grant access. ## GET /v2/stats/volume Returns cumulative volume metrics for your partner account. ### Endpoint ```text theme={null} GET /v2/stats/volume ``` ### Parameters None. ### Example Request ```bash theme={null} curl -X GET "https://api-partner.houdiniswap.com/v2/stats/volume" \ -H "Authorization: :" ``` ### Example Response ```json theme={null} { "count": 1284, "totalTransactedUSD": 4821903.55, "totalOrders": 1284, "thisMonthVolumeUsd": 312450.20, "lastMonthVolumeUsd": 498201.75, "thisMonthOrders": 215 } ``` ### Response Fields | Field | Type | Description | | -------------------- | ------ | ------------------------------------------------------- | | `count` | number | Total completed transactions (all time) | | `totalTransactedUSD` | number | Total transacted value in USD (all time) | | `totalOrders` | number | Alias for `count` | | `thisMonthVolumeUsd` | number | Transacted value in USD for the current calendar month | | `lastMonthVolumeUsd` | number | Transacted value in USD for the previous calendar month | | `thisMonthOrders` | number | Completed transactions in the current calendar month | *** ## GET /v2/stats/weeklyVolume Returns a per-week breakdown of your partner volume, ordered chronologically. ### Endpoint ```text theme={null} GET /v2/stats/weeklyVolume ``` ### Parameters None. ### Example Request ```bash theme={null} curl -X GET "https://api-partner.houdiniswap.com/v2/stats/weeklyVolume" \ -H "Authorization: :" ``` ### Example Response ```json theme={null} [ { "week": 15, "year": 2026, "count": 87, "anonymous": 34, "volume": 41250.00, "commission": 103.13 }, { "week": 16, "year": 2026, "count": 112, "anonymous": 45, "volume": 58930.50, "commission": 147.33 } ] ``` ### Response Fields | Field | Type | Description | | ------------ | ------ | ------------------------------------------ | | `week` | number | ISO week number (1–53) | | `year` | number | Year | | `count` | number | Total transactions for the week | | `anonymous` | number | Number of private (anonymous) transactions | | `volume` | number | Total transaction volume in USD | | `commission` | number | Commission earned in USD for the week | *** ## GET /v2/stats/chart Returns time-series data for a given metric over a custom date range. Useful for building volume charts in dashboards or analytics tools. ### Endpoint ```text theme={null} GET /v2/stats/chart ``` ### Parameters | Field | Type | Required | Description | | ------------- | ------ | -------- | ------------------------------------------------------ | | `metric` | string | Yes | Metric to chart. Currently supported: `volume` | | `from` | string | Yes | Start of range (ISO 8601, e.g. `2026-01-01T00:00:00Z`) | | `to` | string | Yes | End of range (ISO 8601, e.g. `2026-04-22T23:59:59Z`) | | `granularity` | string | No | Bucket size: `day` (default) or `month` | ### Example Request ```bash theme={null} curl -X GET "https://api-partner.houdiniswap.com/v2/stats/chart?metric=volume&from=2026-03-23T00:00:00Z&to=2026-04-22T23:59:59Z&granularity=day" \ -H "Authorization: :" ``` ### Example Response ```json theme={null} [ { "date": "2026-03-23", "volumeUsd": 12430.50, "count": 28 }, { "date": "2026-03-24", "volumeUsd": 9870.00, "count": 21 }, { "date": "2026-03-25", "volumeUsd": 15210.75, "count": 35 } ] ``` ### Response Fields | Field | Type | Description | | ----------- | ------ | ------------------------------------------------------------------ | | `date` | string | Bucket date. Format: `YYYY-MM-DD` for `day`, `YYYY-MM` for `month` | | `volumeUsd` | number | Total transacted value in USD for the bucket | | `count` | number | Number of completed transactions in the bucket | Days or months with no activity are omitted from the response. Your charting code should fill in zero values for missing dates. *** ## Code Example Fetch all three stats endpoints and log a summary: ```typescript theme={null} const BASE = "https://api-partner.houdiniswap.com/v2"; const AUTH = `${process.env.PARTNER_ID}:${process.env.API_SECRET}`; const headers = { Authorization: AUTH }; // Volume summary const volumeRes = await fetch(`${BASE}/stats/volume`, { headers }); const volume = await volumeRes.json(); console.log(`All-time: ${volume.count} orders / $${volume.totalTransactedUSD.toFixed(2)}`); console.log(`This month: ${volume.thisMonthOrders} orders / $${volume.thisMonthVolumeUsd.toFixed(2)}`); // Last 30 days chart (daily granularity) const to = new Date().toISOString(); const from = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(); const chartRes = await fetch( `${BASE}/stats/chart?metric=volume&from=${from}&to=${to}&granularity=day`, { headers } ); const chart = await chartRes.json(); console.log(`Chart data points: ${chart.length}`); // Weekly breakdown const weeklyRes = await fetch(`${BASE}/stats/weeklyVolume`, { headers }); const weekly = await weeklyRes.json(); const latestWeek = weekly[weekly.length - 1]; console.log(`Week ${latestWeek.week}/${latestWeek.year}: $${latestWeek.volume} volume, $${latestWeek.commission} commission`); ``` # Payout Wallet Rotation Source: https://docs.houdiniswap.com/developer-hub/core-concepts/payout-wallet-rotation Improve swap privacy by rotating provider routing paths across consecutive orders # Payout Wallet Rotation Rotate swap routing paths across consecutive orders so that repeated swaps from the same user don't always flow through the same provider wallet. ## Overview When a user makes multiple swaps, Houdini normally selects the best-priced route each time. In practice, this often means the same provider handles consecutive swaps — creating a pattern that reduces privacy. Payout wallet rotation solves this by deprioritizing recently used provider paths and selecting the next-best route instead, while keeping the price within an acceptable range. Rotation applies to **CEX swaps only** (standard and private). DEX swaps execute on-chain through smart contracts and are not affected by provider path selection. ## How It Works You request quotes with `rotatePayoutWallets=true`. Houdini fetches quotes from all available providers as usual. The system checks your recent orders (last N orders within 24 hours) to find which provider paths were already used. Quotes matching recently used paths are moved down in the ranking. Quotes using fresh (not recently used) paths are promoted to the top. If the best non-recently-used quote deviates more than the allowed threshold from the original best quote, rotation is skipped and the best-priced route is used instead. You never pay significantly more for rotation. ## Parameters Enable payout wallet rotation. When `true`, the system deprioritizes recently used provider paths in the quote ranking. Maximum allowed price deviation percentage. If the best rotated quote is more than this percentage worse than the original best quote, rotation is skipped. Range: 0–100. Number of recent orders to check for previously used paths. Valid range: 2–99. ## Lookup Priority The system determines "recently used paths" based on the following priority: | Priority | Identifier | When used | | -------- | ---------------------- | ------------------------------------------------------------------ | | 1 | `multiId` | MultiSwap orders — checks all orders in the same multi-swap group | | 2 | Partner ID | Partner API calls — checks recent orders from your partner account | | 3 | `receiverAddress` + IP | Anonymous/private orders — checks by destination address and IP | For partner integrations, your partner ID is automatically used (priority 2). You don't need to pass any additional identifiers. ## Price Protection Rotation never forces a significantly worse price. The `deviationThreshold` parameter controls the maximum acceptable price difference: * **Deviation ≤ threshold**: Rotated quote is used (different provider path) * **Deviation > threshold**: Rotation is skipped, best-priced quote is returned * **All quotes recently used**: Original ranking is preserved (no rotation possible) **Example**: With `deviationThreshold=5`, if the best quote returns 10 ETH and the best *non-recently-used* quote returns 9.6 ETH (4% worse), the rotated quote is used. If it returns 9.4 ETH (6% worse), rotation is skipped. ## API v2 Usage In v2, rotation is configured at **quote time**. The rotation parameters are query params on `GET /quotes`, and the selected quote carries the rotation context through to the exchange. ### Step 1: Get Quotes with Rotation ```javascript Node.js theme={null} const params = new URLSearchParams({ amount: '1', from: '6689b73ec90e45f3b3e51566', // ETH token id to: '6689b73ec90e45f3b3e51577', // SOL token id types: 'private', rotatePayoutWallets: 'true', deviationThreshold: '5', rotationLookback: '10', receiverAddress: '1nc1nerator11111111111111111111111111111111' }); const response = await fetch( `https://api-partner.houdiniswap.com/v2/quotes?${params}`, { headers: { 'Authorization': `${API_KEY}:${API_SECRET}` } } ); const { quotes } = await response.json(); // Quotes are already re-ranked with rotation applied const bestQuote = quotes[0]; ``` ```bash cURL theme={null} curl -G "https://api-partner.houdiniswap.com/v2/quotes" \ -H "Authorization: YOUR_PARTNER_ID:YOUR_SECRET" \ -d "amount=1" \ -d "from=6689b73ec90e45f3b3e51566" \ -d "to=6689b73ec90e45f3b3e51577" \ -d "types=private" \ -d "rotatePayoutWallets=true" \ -d "deviationThreshold=5" \ -d "rotationLookback=10" \ -d "receiverAddress=1nc1nerator11111111111111111111111111111111" ``` ### Step 2: Create Exchange No additional parameters needed — the quote already has rotation applied. ```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' }, body: JSON.stringify({ quoteId: bestQuote.quoteId, addressTo: '1nc1nerator11111111111111111111111111111111' }) }); ``` ## API v1 Usage In v1, rotation can be configured on both the quote and exchange endpoints. ### Option A: Rotation at Quote Time Pass rotation parameters as query params on `GET /quote`: ```javascript Node.js theme={null} const params = new URLSearchParams({ amount: '1', from: 'ETH', to: 'BNB', anonymous: 'true', useXmr: 'false', rotatePayoutWallets: 'true', deviationThreshold: '5', rotationLookback: '10' }); const response = await fetch( `https://api-partner.houdiniswap.com/quote?${params}`, { headers: { 'Authorization': `${API_KEY}:${API_SECRET}` } } ); ``` ```bash cURL theme={null} curl -G "https://api-partner.houdiniswap.com/quote" \ -H "Authorization: YOUR_PARTNER_ID:YOUR_SECRET" \ -d "amount=1" \ -d "from=ETH" \ -d "to=BNB" \ -d "anonymous=true" \ -d "useXmr=false" \ -d "rotatePayoutWallets=true" \ -d "deviationThreshold=5" \ -d "rotationLookback=10" ``` ### Option B: Rotation at Exchange Time Pass a `filters` object in the `POST /exchange` request body: ```javascript Node.js theme={null} const response = await fetch('https://api-partner.houdiniswap.com/exchange', { method: 'POST', headers: { 'Authorization': `${API_KEY}:${API_SECRET}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ amount: 1, from: 'ETH', to: 'BNB', addressTo: '0x000000000000000000000000000000000000dead', anonymous: true, useXmr: false, ip: '203.0.113.1', userAgent: 'Mozilla/5.0', timezone: 'UTC', filters: { rotatePayoutWallets: true, deviationThreshold: 5, rotationLookback: 10 } }) }); ``` ```bash cURL theme={null} curl -X POST "https://api-partner.houdiniswap.com/exchange" \ -H "Authorization: YOUR_PARTNER_ID:YOUR_SECRET" \ -H "Content-Type: application/json" \ -d '{ "amount": 1, "from": "ETH", "to": "BNB", "addressTo": "0x000000000000000000000000000000000000dead", "anonymous": true, "useXmr": false, "ip": "203.0.113.1", "userAgent": "Mozilla/5.0", "timezone": "UTC", "filters": { "rotatePayoutWallets": true, "deviationThreshold": 5, "rotationLookback": 10 } }' ``` When `rotatePayoutWallets` is enabled on `POST /exchange` in v1, any `inQuoteId` and `outQuoteId` values are discarded. The system fetches fresh quotes with rotation applied at exchange time. This adds a few seconds to the exchange request. ## v1 vs v2 Behavior | Aspect | v1 | v2 | | ------------------------------------- | ------------------------------------------------- | ---------------------------------------------------- | | **Where rotation is configured** | Quote endpoint, exchange endpoint, or both | Quote endpoint only | | **How rotation reaches the exchange** | `filters` object passed directly in exchange body | Inherited from the selected quote via `quoteId` | | **Quote IDs with rotation** | Discarded when rotation is enabled on exchange | Preserved — rotation is already applied to the quote | | **Latency impact** | Adds \~3-5s when used on exchange (re-quotes) | No additional latency on exchange | For v1 integrations, we recommend using rotation at **quote time** (Option A) rather than exchange time. This avoids the re-quoting overhead and gives you visibility into the rotated quotes before committing to an exchange. ## Best Practices Enable rotation for private/anonymous swaps where users make repeated swaps. For one-off standard swaps, rotation adds little value. The default 5% threshold balances privacy with price quality. Setting it too high (e.g., 20%) may route through significantly worse-priced providers. Setting it too low (e.g., 1%) may prevent rotation entirely when quotes are tightly clustered. If your integration processes many swaps per hour, increase `rotationLookback` to 20–30 to ensure broader path diversity. For low-volume integrations, the default of 10 is sufficient. Use the `swaps` parameter (v2) or `onlySwaps` filter (v1) alongside rotation to restrict which providers are considered. Rotation will only rotate within the allowed provider set. ## Next Steps Full guide for integrating private multi-hop swaps Understand the three routing strategies # Routing Types Source: https://docs.houdiniswap.com/developer-hub/core-concepts/routing-types Understand Houdini's routing strategies — private, standard, DEX, and fixed rate — and when to use each ## Overview Houdini supports three core routing strategies plus a fixed rate variant for standard swaps. You can choose to support one, multiple, or all routing types in your integration. ## Routing Strategy Comparison | Feature | Private Swap | Standard Swap | Fixed Rate Standard | DEX Swap | | --------------------- | --------------------- | --------------- | --------------------- | ----------------------------- | | **Privacy Level** | Highest | High | High | Public/Transparent | | **Wallet Connection** | Not required | Not required | Not required | Required | | **Speed** | 15-45 minutes | 3-30 minutes | 3-30 minutes | Variable (seconds to minutes) | | **Gas Fees** | None (included) | None (included) | None (included) | User pays | | **Liquidity** | High (CEX depth) | Very High | Very High | Variable | | **Rate Guarantee** | No | No | Yes — locked at quote | No (slippage applies) | | **Best For** | Privacy-focused users | Fast execution | Price certainty | On-chain transparency | ## 1. Private Swap ### How It Works Private swaps route through **multiple CEX hops** to maximize privacy and eliminate the need for wallet connections. ```mermaid theme={null} graph LR A[User Deposit] --> B[CEX 1] B --> C[CEX 2] C --> D[Destination Wallet] style A fill:#e1f5ff,color:#000 style D fill:#e1f5ff,color:#000 style B fill:#fff3cd,color:#000 style C fill:#fff3cd,color:#000 ``` User sends funds to Houdini-provided deposit address Funds route through 2 partner exchanges to break transaction trail Final token arrives at user's destination address ### Technical Flow for Integrators For detailed integration steps, see the [Private Swap Integration Guide](/developer-hub/swap-flows/private-swap). ### Pros and Cons * **Maximum privacy**: Multi-hop breaks transaction trail * **No wallet needed**: Users just send to deposit address * **No gas fees**: All fees included in quote * **Deep liquidity**: Access to CEX order books * **Longer completion time**: 15-45 minutes typical * **CEX dependency**: Relies on partner exchange availability * **AML screening**: Transactions screened by partner CEXs * **Not instant**: Not suitable for time-critical swaps ### When to Use * Users prioritize privacy and anonymity * No wallet connection is possible or desired * Time is not critical (15-45 min acceptable) * Swapping larger amounts where CEX liquidity shines ## 2. Standard Swap (No Wallet Connect) ### How It Works Semi-private swaps use a **single CEX hop** for faster execution while maintaining privacy. ```mermaid theme={null} graph LR A[User Deposit] --> B[CEX Partner] B --> C[Destination Wallet] style A fill:#e1f5ff,color:#000 style C fill:#e1f5ff,color:#000 style B fill:#fff3cd,color:#000 ``` User sends funds to deposit address Funds route through one partner exchange Faster settlement to destination address (3-30 minutes) ### Technical Flow for Integrators For detailed integration steps, see the [Standard Swap Integration Guide](/developer-hub/swap-flows/standard-swap). Standard swaps support a **fixed rate variant** — add `fixed=true` to your `/quotes` request to lock the rate and guarantee the output amount. See [Fixed Rate Standard Swap](#4-fixed-rate-standard-swap) below. ### Pros and Cons * **Faster than multi-hop**: 3-30 minutes typical * **No wallet needed**: Deposit address flow * **No gas fees**: Included in quote * **Excellent liquidity**: CEX order book depth * **Less privacy**: Single hop vs multi-hop * **CEX dependency**: Relies on partner availability * **AML screening**: CEX compliance applies * **Not instant**: Still takes 3-30 minutes ### When to Use * Balance between speed and privacy * Faster execution preferred (3-30 min vs 15-45 min) * No wallet connection needed * Good liquidity required ## 3. Fixed Rate Standard Swap ### How It Works Fixed rate is a variant of the standard swap that **locks the exchange rate at quote time**. The user receives exactly the quoted `amountOut` regardless of market movement, as long as the exchange is created before `validUntil` expires. ```mermaid theme={null} graph LR A[User Deposit] --> B[CEX Partner] B --> C[Destination Wallet] style A fill:#e1f5ff,color:#000 style C fill:#e1f5ff,color:#000 style B fill:#d4edda,color:#000 ``` ### Technical Flow for Integrators For detailed integration steps, see the [Fixed Rate Swap Integration Guide](/developer-hub/swap-flows/fixed-rate-swap). ### Pros and Cons * **Guaranteed output**: Exact `amountOut` delivered regardless of volatility * **No wallet needed**: Same deposit-address flow as standard * **No gas fees**: Included in quote * **Same speed**: 3-30 minutes like standard swaps * **Rate lock window**: Must create the exchange before `validUntil` * **refundAddress required**: Must supply a source-chain refund wallet * **Provider-bound**: Rate tied to one provider — no fallback if provider goes down * **CEX only**: Not available for DEX or private swaps ### When to Use * User needs a guaranteed output amount (e.g. paying an exact invoice) * High-value swaps where slippage is unacceptable * Partner wants to show a firm quote in the UI before the user commits ## 4. DEX Swap ### How It Works DEX swaps execute **directly on-chain** through decentralized exchanges and bridges. ```mermaid theme={null} graph LR A[User Wallet] --> B[DEX/Bridge] B --> C[Destination Wallet] style A fill:#e1f5ff,color:#000 style C fill:#e1f5ff,color:#000 style B fill:#d4edda,color:#000 ``` Wallet approves token spending (if needed) Transaction sent to DEX or bridge contract Swap completes on-chain, funds arrive at destination ### Technical Flow for Integrators For detailed integration steps, see the [DEX Swap Integration Guide](/developer-hub/swap-flows/dex-swap). ### Pros and Cons * **Fully on-chain**: Transparent and verifiable * **No CEX dependency**: Pure DeFi execution * **Can be fast**: Seconds to minutes depending on network * **Trustless**: Smart contract execution * **Wide token support**: Access to long-tail assets * **Public**: All transactions visible on-chain * **Requires wallet**: User must connect wallet * **User pays gas**: Network fees additional * **Variable gas costs**: Can be expensive on Ethereum L1 * **Slippage risk**: Especially for large trades or volatile pairs ### When to Use * On-chain transparency required or preferred * Access to long-tail or newer tokens * Lower gas fee networks (L2s, alt-L1s) * Users already have wallet connected * Trustless execution is priority ## Next Steps Understand order states and status transitions Detailed integration guide for private swaps Guaranteed output — rate locked at quote time Complete guide for DEX swap integration Full API documentation for all endpoints # Tokens & Networks Source: https://docs.houdiniswap.com/developer-hub/core-concepts/tokens-networks Understanding how tokens and networks are represented and discovered in Houdini * **Tokens**: Use token `id` values to request quotes via [`/quotes`](/developer-hub/swap-flows/standard-swap) for all swap types (standard, private, DEX). * **Networks**: Use network data for UI display (logos, names, explorer links) and address validation. *** # Tokens ## Token Identifiers In API v2, both CEX and DEX tokens are fetched from the same `/tokens` endpoint. Every token has a single `id` field (MongoDB ObjectId format) used in all quote requests. * **CEX Tokens** — filter with `hasCex=true` (used for standard and private swaps) * **DEX Tokens** — filter with `hasDex=true` (used for on-chain DEX swaps) ### Token Fields | Field | Type | Description | Example | | ---------- | ------- | ---------------------------------------------------- | ------------------------------------------ | | `id` | string | **Required for `/quotes`** — Unique token identifier | `"6689b73ec90e45f3b3e51566"` | | `symbol` | string | Token ticker symbol | `"ETH"` | | `name` | string | Full token name | `"Ethereum"` | | `address` | string | Token contract address | `"0x0000...0000"` | | `chain` | string | Blockchain network shortName | `"ethereum"` | | `decimals` | number | Token decimals | `18` | | `icon` | string | Token logo URL | `"https://api.houdiniswap.com/assets/..."` | | `hasDex` | boolean | Available for DEX swaps | `true` | | `hasCex` | boolean | Available for CEX swaps | `true` | | `enabled` | boolean | Whether token is available for swaps | `true` | | `price` | number | Current USD price | `2937` | Always use the `id` field (ObjectId format) when passing tokens to `/quotes`. Passing symbols will not work. ## Fetching Tokens There are two approaches — choose the one that fits your integration: ### Bulk Fetch + Cache (Recommended for Production) Paginate through all tokens and store in your database. Refresh every 24 hours. ```javascript JavaScript theme={null} async function fetchAllCexTokens() { let page = 1; let allTokens = []; while (true) { const response = await fetch( `https://api-partner.houdiniswap.com/v2/tokens?hasCex=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?hasCex=true&pageSize=20&page=1" \ -H "Authorization: your_api_key:your_api_secret" ``` ```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" ``` ### Token Search (On-Demand) Search by name or symbol using the `term` parameter. ```javascript JavaScript theme={null} async function searchTokens(query, type = 'hasCex') { const params = new URLSearchParams({ term: query, // e.g. "ethereum" or "ETH" [type]: '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&hasCex=true&pageSize=20&page=1" \ -H "Authorization: your_api_key:your_api_secret" ``` **Query Parameters**: | Parameter | Description | | ---------- | --------------------------------------------- | | `hasCex` | Filter to CEX-supported tokens | | `hasDex` | Filter to DEX-supported tokens | | `term` | Search by name, symbol, or address | | `chain` | Filter by chain shortName (e.g. `"ethereum"`) | | `page` | Page number (default: 1) | | `pageSize` | Results per page (max: 100, default: 20) | **Example Response**: ```json theme={null} { "tokens": [ { "id": "6689b73ec90e45f3b3e51566", "symbol": "ETH", "name": "Ethereum", "address": "0x0000000000000000000000000000000000000000", "chain": "ethereum", "decimals": 18, "icon": "https://api.houdiniswap.com/assets/tokens/ETH.png", "hasCex": true, "hasDex": true, "enabled": true, "price": 2937 } ], "total": 1, "totalPages": 1 } ``` ## Using Token IDs in Quotes Pass the `id` from the token response as `from` and `to` parameters in `/quotes`: ```javascript theme={null} // ETH token id from /tokens response const ethId = '6689b73ec90e45f3b3e51566'; // SOL token id from /tokens response const solId = '6689b73ec90e45f3b3e51577'; const params = new URLSearchParams({ amount: '1', from: ethId, to: solId, types: 'standard' // or 'private', 'dex' }); const response = await fetch( `https://api-partner.houdiniswap.com/v2/quotes?${params}`, { headers: { 'Authorization': `${API_KEY}:${API_SECRET}` } } ); const { quotes } = await response.json(); ``` *** # Networks (Chains) ## Chain Identifiers Houdini exposes supported blockchain networks via the `/chains` endpoint. Use `shortName` to filter tokens by chain and for address validation. | Field | Type | Description | Example | | ------------------- | ------- | ------------------------------------- | ------------------------------------------ | | `id` | string | Unique chain identifier | `"507f1f77bcf86cd799439011"` | | `name` | string | Full network name | `"Ethereum Mainnet"` | | `shortName` | string | Short name — use for chain filtering | `"ethereum"` | | `chainId` | number | EVM chain ID (EVM chains only) | `1` | | `kind` | string | Network type | `"evm"` | | `memoNeeded` | boolean | Whether a memo/tag is required | `false` | | `explorerUrl` | string | Transaction explorer URL template | `"https://etherscan.io/tx/{txHash}"` | | `addressUrl` | string | Address explorer URL template | `"https://etherscan.io/address/{address}"` | | `addressValidation` | string | Regex for validating wallet addresses | `"^(0x)[0-9A-Za-z]{40}$"` | ## Fetching Chains ```javascript JavaScript theme={null} const response = await fetch( 'https://api-partner.houdiniswap.com/v2/chains', { headers: { 'Authorization': `${API_KEY}:${API_SECRET}` } } ); const { chains } = await response.json(); const enabledChains = chains.filter(c => c.enabled ?? true); ``` ```bash cURL theme={null} curl -X GET "https://api-partner.houdiniswap.com/v2/chains" \ -H "Authorization: your_api_key:your_api_secret" ``` **Query Parameters**: | Parameter | Description | | ------------------- | ------------------------------------------------------ | | `hasCex` | Filter to chains supporting CEX swaps | | `hasDex` | Filter to chains supporting DEX swaps | | `kind` | Filter by type: `"evm"`, `"solana"`, `"bitcoin"`, etc. | | `name` | Search by name or shortName | | `page` / `pageSize` | Pagination | **Example Response**: ```json theme={null} { "chains": [ { "id": "507f1f77bcf86cd799439011", "name": "Ethereum Mainnet", "shortName": "ethereum", "chainId": 1, "kind": "evm", "memoNeeded": false, "explorerUrl": "https://etherscan.io/tx/{txHash}", "addressUrl": "https://etherscan.io/address/{address}", "addressValidation": "^(0x)[0-9A-Za-z]{40}$", "tokenAddressValidation": "^(0x)[0-9A-Za-z]{40}$" } ], "total": 1, "totalPages": 1 } ``` ## Using Networks ### Validate Addresses Use the `addressValidation` regex to validate user input before submitting: ```javascript theme={null} function validateAddress(chain, address) { const regex = new RegExp(chain.addressValidation); if (!regex.test(address)) { throw new Error(`Invalid address for ${chain.name}`); } return true; } ``` ### Generate Explorer Links ```javascript theme={null} function getTransactionUrl(chain, txHash) { return chain.explorerUrl.replace('{txHash}', txHash); } function getAddressUrl(chain, address) { return chain.addressUrl.replace('{address}', address); } ``` ### Check Memo Requirements ```javascript theme={null} function checkMemoRequirement(chain) { if (chain.memoNeeded) { return { required: true, message: `${chain.name} requires a memo/tag` }; } return { required: false }; } ``` *** ## Next Steps Learn how to use DEX tokens in on-chain swaps Integrate standard CEX swaps using the tokens endpoint Implement private swaps with CEX tokens # Order Status Updates Source: https://docs.houdiniswap.com/developer-hub/core-concepts/websocket-order-updates Real-time order status updates via WebSocket # WebSocket: Order Status Updates Get instant order status updates pushed to your application in real-time instead of polling the REST API. ## Connection ``` wss://partner-api.houdiniswap.com/v2/ws ``` ### Authentication Include your API credentials in the `Authorization` header during the WebSocket handshake: ``` Authorization: {partnerId}:{secret} ``` The server performs a quick format check on the header before upgrading the connection. If the header is missing or malformed, the connection is rejected at the HTTP level: | HTTP Code | Reason | | --------- | --------------------------------------------- | | 401 | Missing `Authorization` header | | 400 | Malformed header (must be `partnerId:secret`) | Once the WebSocket connection is established, the server verifies your credentials asynchronously. On success you receive a `welcome` message. If credentials are invalid, you receive an `error` message with code `AUTH_FAILED` and the connection is closed with WebSocket close code `4401`. ## Quick Start ```javascript Node.js theme={null} // Note: Custom headers require a Node.js WebSocket client (e.g. 'ws' npm package). // Browser-native WebSocket does not support custom headers. const WebSocket = require("ws"); const ws = new WebSocket("wss://partner-api.houdiniswap.com/v2/ws", { headers: { Authorization: "your-partner-id:your-secret" }, }); ws.onopen = () => { // Subscribe to all your orders ws.send(JSON.stringify({ type: "subscribe" })); }; ws.onmessage = (event) => { const message = JSON.parse(event.data); switch (message.type) { case "welcome": console.log("Connected as", message.partnerId); break; case "order_update": console.log("Order updated:", message.data.houdiniId, "→", message.data.status); break; case "subscribed": console.log("Subscribed to:", message.houdiniIds); break; } }; ``` ```python Python theme={null} import asyncio import json import websockets async def listen(): headers = {"Authorization": "your-partner-id:your-secret"} async with websockets.connect( "wss://partner-api.houdiniswap.com/v2/ws", additional_headers=headers, ) as ws: # Subscribe to all orders await ws.send(json.dumps({"type": "subscribe"})) async for raw in ws: message = json.loads(raw) if message["type"] == "order_update": data = message["data"] print(f"Order {data['houdiniId']} → status {data['status']}") asyncio.run(listen()) ``` ```bash wscat theme={null} wscat -H "Authorization: your-partner-id:your-secret" \ -c wss://partner-api.houdiniswap.com/v2/ws # Once connected, subscribe to all orders: > {"type":"subscribe"} ``` ## Message Protocol All messages are JSON objects with a `type` field. ### Client → Server #### subscribe Subscribe to order status updates. Send without `houdiniIds` to receive updates for **all** your orders, or specify an array to watch specific orders. ```json Subscribe to all orders theme={null} { "type": "subscribe" } ``` ```json Subscribe to specific orders theme={null} { "type": "subscribe", "houdiniIds": ["abc123", "def456"] } ``` Subscriptions are additive. Sending multiple `subscribe` messages with different `houdiniIds` adds to your watch list. Sending a `subscribe` without `houdiniIds` switches to all-orders mode. #### unsubscribe Stop receiving updates. Send without `houdiniIds` to unsubscribe from everything, or specify an array to stop watching specific orders. ```json Unsubscribe from all theme={null} { "type": "unsubscribe" } ``` ```json Unsubscribe from specific orders theme={null} { "type": "unsubscribe", "houdiniIds": ["abc123"] } ``` If you are subscribed to all orders and send an `unsubscribe` with specific `houdiniIds`, this resets your subscription to none. To continue receiving updates for other orders, re-subscribe after unsubscribing. #### ping Application-level keepalive. The server responds with `pong`. ```json theme={null} { "type": "ping" } ``` The server also sends WebSocket-level pings every 30 seconds. Most WebSocket libraries handle these automatically. The application-level `ping`/`pong` is optional — use it if you want to measure round-trip latency. ### Server → Client #### welcome Sent immediately after a successful connection. ```json theme={null} { "type": "welcome", "partnerId": "your-partner-id" } ``` #### order\_update Pushed whenever an order's status changes. The `data` field contains the full order object — the same shape as `GET /v2/orders/{houdiniId}`. ```json theme={null} { "type": "order_update", "data": { "houdiniId": "abc123", "status": 2, "inStatus": 3, "outStatus": null, "amount": "0.5", "amountTo": "150.25", "inToken": { "symbol": "ETH", "network": "ethereum" }, "outToken": { "symbol": "USDC", "network": "ethereum" }, "receiverAddress": "0x...", "created": "2026-01-15T10:30:00.000Z", "expires": "2026-01-15T11:00:00.000Z" } } ``` #### subscribed Confirmation after a `subscribe` message. ```json theme={null} { "type": "subscribed", "houdiniIds": ["abc123", "def456"] } ``` ```json When subscribed to all orders theme={null} { "type": "subscribed", "houdiniIds": "all" } ``` #### unsubscribed Confirmation after an `unsubscribe` message. ```json When unsubscribed from all theme={null} { "type": "unsubscribed", "houdiniIds": "all" } ``` ```json When unsubscribed from specific orders theme={null} { "type": "unsubscribed", "houdiniIds": ["abc123"] } ``` #### error Sent when the server cannot process a client message. ```json theme={null} { "type": "error", "code": "INVALID_MESSAGE", "message": "Invalid JSON" } ``` | Error Code | Description | | ---------------------- | ---------------------------------------------------------------- | | `AUTH_FAILED` | Invalid credentials — connection will be closed with code `4401` | | `INVALID_MESSAGE` | Message is not valid JSON or missing `type` field | | `UNKNOWN_MESSAGE_TYPE` | The `type` field is not recognized | ## Order Status Codes The `status` field in `order_update` is a numeric code: | Code | Label | Description | | ---- | ------------ | -------------------------------------- | | -2 | INITIALIZING | Order is being initialized | | -1 | NEW | Order initialized, awaiting processing | | 0 | WAITING | Waiting for user's deposit | | 1 | CONFIRMING | Deposit is being confirmed on-chain | | 2 | EXCHANGING | Swap is in progress | | 3 | ANONYMIZING | Going through privacy routing | | 4 | FINISHED | Completed successfully | | 5 | EXPIRED | Expired (no deposit within 30 minutes) | | 6 | FAILED | Failed | | 7 | REFUNDED | Refunded to sender | | 8 | DELETED | Deleted (order older than 48 hours) | **Terminal statuses** (no further updates): `FINISHED`, `EXPIRED`, `FAILED`, `REFUNDED`, `DELETED`. ## Reconnection The server does not persist subscriptions across disconnects. If your connection drops: 1. Reconnect with credentials 2. Re-send your `subscribe` message 3. Optionally call `GET /v2/orders` to catch any updates you missed **Recommended strategy**: exponential backoff starting at 1 second, capped at 30 seconds. ```javascript theme={null} let reconnectDelay = 1000; const connect = () => { const ws = new WebSocket(url, { headers }); ws.onopen = () => { reconnectDelay = 1000; // Reset on successful connection ws.send(JSON.stringify({ type: "subscribe" })); }; ws.onclose = () => { setTimeout(connect, reconnectDelay); reconnectDelay = Math.min(reconnectDelay * 2, 30000); }; }; connect(); ``` ## Security Notes * Only orders belonging to your partner account are delivered. You cannot receive updates for other partners' orders. * The `Authorization` header format is validated during the HTTP upgrade. Credentials are verified immediately after the WebSocket connection is established. * Anonymous/private swap orders have their routing path censored from the response. # x402 Payments Source: https://docs.houdiniswap.com/developer-hub/core-concepts/x402-pay-per-request Pay-per-request API access using USDC stablecoin payments # x402: Pay-Per-Request API Access HoudiniSwap Partner API v2 supports the [x402 payment protocol](https://docs.cdp.coinbase.com/x402/welcome) as an alternative to traditional API key authentication. With x402, you pay for each API request using USDC on supported EVM chains — no account registration or API key required. ## How It Works The x402 protocol uses the HTTP `402 Payment Required` status code to enable pay-per-request access: ``` Client HoudiniSwap API Facilitator | | | | 1. GET /v2/tokens | | |--------------------------->| | | 2. 402 Payment Required | | | (PAYMENT-REQUIRED header)| | |<---------------------------| | | | | | 3. Sign USDC authorization| | | (EIP-3009, gasless) | | | | | | 4. GET /v2/tokens | | | (PAYMENT-SIGNATURE hdr) | | |--------------------------->| | | | 5. Verify & settle | | |-------------------------->| | | 6. Settlement confirmed | | |<--------------------------| | 7. 200 OK (data) | | |<---------------------------| | ``` 1. Client makes a request without authentication 2. Server responds with `402` and a `PAYMENT-REQUIRED` header containing payment instructions 3. Client signs a USDC `transferWithAuthorization` (EIP-3009) — **no gas required** 4. Client retries the request with the signed payment in the `PAYMENT-SIGNATURE` header 5. Server forwards the payment to the facilitator for on-chain verification and settlement 6. Facilitator confirms the USDC transfer 7. Server returns the requested data ## Supported Networks | Network | CAIP-2 ID | USDC Contract | | -------------- | ------------- | -------------------------------------------- | | Base (default) | `eip155:8453` | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | Additional EVM networks (Ethereum, Polygon, Arbitrum) can be enabled — contact us for availability. ## Pricing Requests are priced by operation type in USDC: | Operation | Price (USDC) | Endpoints | | ------------ | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | **Read** | \$0.0001 | `GET /tokens`, `GET /chains`, `GET /swaps`, `GET /minMax`, `GET /rateLimits`, `POST /dex/approve`, `POST /dex/allowance`, `POST /dex/chainSignatures` | | **Quote** | \$0.001 | `GET /quotes`, `GET /quotes/byChainAddress` | | **Exchange** | \$0.01 | `POST /exchanges`, `POST /exchanges/multi`, `GET /exchanges/multi/{id}/tx`, `POST /dex/confirmTx` | | **Status** | \$0.0001 | `GET /exchanges/multi/{id}`, `GET /orders`, `GET /orders/{id}` | `GET /v2/health` and `GET /v2/openapi.json` are always free and do not require payment or authentication. ## Rate Limits x402 payers are rate-limited to **60 requests per minute** per payer address. This is separate from API key rate limits. ## x402 vs API Key Authentication | Feature | x402 | API Key | | --------------------- | -------------------------- | ------------------------ | | Registration required | No | Yes | | Pay model | Per-request | Subscription/tier | | Authentication | USDC payment | `Authorization` header | | Rate limits | 60 req/min per address | Tier-based | | Best for | Agents, bots, ad-hoc usage | High-volume integrations | When both are available, API key authentication takes priority. If a request includes an `Authorization` header (full API key access) or a `partner-id` header (public read-only partner access), the x402 payment flow is bypassed entirely. ## Quick Start ### Prerequisites * An EVM wallet with USDC on a supported network * Node.js 18+ (for the JavaScript client) * USDC on Base (amounts are tiny — a full exchange flow costs \~\$0.012 total) ### 1. Install Dependencies ```bash theme={null} npm install @x402/fetch @x402/evm viem ``` ### 2. Create a Payment Client ```typescript theme={null} import { x402Client, x402HTTPClient } from "@x402/fetch"; import { registerExactEvmScheme } from "@x402/evm/exact/client"; import { privateKeyToAccount } from "viem/accounts"; // Create signer from your wallet's private key const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`); // Create x402 client and register EVM payment scheme const client = new x402Client(); registerExactEvmScheme(client, { signer }); const httpClient = new x402HTTPClient(client); ``` ### 3. Make Paid API Requests ```typescript theme={null} const BASE_URL = "https://api-partner.houdiniswap.com"; const x402Fetch = async (url: string, options: RequestInit = {}) => { // Initial request const res = await fetch(url, options); if (res.status !== 402) return res; // Parse payment requirements from 402 response const paymentRequired = httpClient.getPaymentRequiredResponse( (name) => res.headers.get(name), undefined, ); // Sign the USDC payment (gasless EIP-3009) const payload = await client.createPaymentPayload(paymentRequired); const paymentHeaders = httpClient.encodePaymentSignatureHeader(payload); // Retry with payment proof return fetch(url, { ...options, headers: { ...options.headers, ...paymentHeaders }, }); }; // Get tokens — automatically handles 402 → pay → retry const response = await x402Fetch(`${BASE_URL}/v2/tokens?term=BTC`); const data = await response.json(); console.log(data.tokens); ``` ### 4. Check Payment Receipt Successful paid responses include a `payment-response` header: ```typescript theme={null} const paymentResponse = response.headers.get("payment-response"); if (paymentResponse) { const receipt = JSON.parse(Buffer.from(paymentResponse, "base64").toString()); console.log(receipt); // { // "success": true, // "transaction": "0xabc...def", // "network": "eip155:8453", // "payer": "0x..." // } } ``` ## Full Exchange Flow Example This example demonstrates a complete swap flow using x402 payments: ```typescript theme={null} import { x402Client, x402HTTPClient } from "@x402/fetch"; import { registerExactEvmScheme } from "@x402/evm/exact/client"; import { privateKeyToAccount } from "viem/accounts"; const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`); const client = new x402Client(); registerExactEvmScheme(client, { signer }); const httpClient = new x402HTTPClient(client); const BASE = "https://api-partner.houdiniswap.com/v2"; const x402Fetch = async (url: string, options: RequestInit = {}) => { const res = await fetch(url, options); if (res.status !== 402) return res; const pr = httpClient.getPaymentRequiredResponse((n) => res.headers.get(n), undefined); const payload = await client.createPaymentPayload(pr); const headers = httpClient.encodePaymentSignatureHeader(payload); return fetch(url, { ...options, headers: { ...options.headers, ...headers } }); }; // Step 1: Look up tokens ($0.0001 each) const btcRes = await x402Fetch(`${BASE}/tokens?term=BTC&hasCex=true`); const btcData = await btcRes.json(); const btcToken = btcData.tokens.find((t: any) => t.chain === "bitcoin"); const ethRes = await x402Fetch(`${BASE}/tokens?term=ETH&hasCex=true`); const ethData = await ethRes.json(); const ethToken = ethData.tokens.find((t: any) => t.chain === "ethereum"); // Step 2: Get a quote ($0.001) const quoteRes = await x402Fetch( `${BASE}/quotes?from=${btcToken.id}&to=${ethToken.id}&amount=0.01` ); const quoteData = await quoteRes.json(); const quote = quoteData.quotes[0]; console.log(`Quote: ${quote.amountOut} ETH`); // Step 3: Create exchange ($0.01) const exchangeRes = await x402Fetch(`${BASE}/exchanges`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ quoteId: quote.quoteId, addressTo: "0xYourEthAddress...", }), }); const exchange = await exchangeRes.json(); console.log(`Order: ${exchange.houdiniId}, deposit to: ${exchange.depositAddress}`); // Step 4: Poll status ($0.0001 each) const checkStatus = async () => { const res = await x402Fetch(`${BASE}/orders/${exchange.houdiniId}`); return res.json(); }; let order = await checkStatus(); const TERMINAL = ["FINISHED", "FAILED", "EXPIRED", "REFUNDED", "DELETED"]; while (!TERMINAL.includes(order.statusLabel)) { await new Promise((r) => setTimeout(r, 30000)); order = await checkStatus(); console.log(`Status: ${order.statusLabel}`); } ``` Each x402 payment is an on-chain USDC transfer. Allow sufficient time between rapid sequential requests to avoid transaction failures from nonce collisions. For high-frequency access, consider using [API key authentication](/developer-hub/getting-started/authentication) instead. ## Headers ### x402 Protocol Headers These headers are set by the x402 SDK and carry the payment data: | Header | Direction | Description | | ------------------- | -------------- | ------------------------------------------------------------------------------ | | `PAYMENT-REQUIRED` | Response (402) | Base64-encoded JSON with payment instructions (price, network, payTo address) | | `PAYMENT-SIGNATURE` | Request | Base64-encoded signed payment payload (client sends this on the retry request) | | `payment-response` | Response (200) | Base64-encoded settlement receipt (transaction hash, payer, network) | ### CORS Headers HoudiniSwap adds the x402 headers to CORS allow/expose lists so browser-based clients can access them: | Header | Value added | | ------------------------------- | ----------------------------------------------------------------- | | `Access-Control-Allow-Headers` | `X-PAYMENT` (allows clients to send payment headers) | | `Access-Control-Expose-Headers` | `X-PAYMENT-RESPONSE` (allows clients to read settlement receipts) | The CORS header names (`X-PAYMENT`, `X-PAYMENT-RESPONSE`) are SDK-level aliases. The actual protocol headers your code reads/writes are `PAYMENT-REQUIRED`, `PAYMENT-SIGNATURE`, and `payment-response` as listed above. ## Payment Required Response Format The `PAYMENT-REQUIRED` header decodes to: ```json theme={null} { "x402Version": 2, "error": "Payment required", "resource": { "url": "https://api-partner.houdiniswap.com/v2/tokens", "description": "HoudiniSwap API - GET /tokens", "mimeType": "application/json" }, "accepts": [ { "scheme": "exact", "network": "eip155:8453", "amount": "100", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "payTo": "0x...", "maxTimeoutSeconds": 300, "extra": { "name": "USDC", "version": "2" } } ] } ``` The `amount` field is in USDC atomic units (6 decimals). `100` = \$0.0001 USDC. ## Other Languages The x402 protocol has official client libraries for multiple languages: * **Python**: [`x402`](https://pypi.org/project/x402/) with `httpx` or `requests` * **Go**: [`github.com/coinbase/x402/go`](https://github.com/coinbase/x402/tree/main/go) * **Axios** (Node.js): [`@x402/axios`](https://www.npmjs.com/package/@x402/axios) See the [x402 Quickstart for Buyers](https://docs.cdp.coinbase.com/x402/quickstart-for-buyers) for setup instructions in each language. ## Error Handling | Scenario | Response | Action | | ---------------------------- | ----------------------------------- | --------------------------------- | | No auth header, x402 enabled | `402` with `PAYMENT-REQUIRED` | Sign payment and retry | | Payment signature invalid | `402` with `payment-response` error | Check signing key and payload | | Facilitator unavailable | `503 Service Unavailable` | Retry later or use API key auth | | Rate limit exceeded (x402) | `429 Too Many Requests` | Wait and retry (60 req/min limit) | | API key present | Normal auth flow | x402 is bypassed entirely | ## Further Reading * [x402 Protocol Overview](https://docs.cdp.coinbase.com/x402/welcome) — Official protocol specification * [x402 Quickstart for Buyers](https://docs.cdp.coinbase.com/x402/quickstart-for-buyers) — Client setup in all languages * [x402 Network Support](https://docs.cdp.coinbase.com/x402/network-support) — Supported chains and assets # API Keys & Authentication Source: https://docs.houdiniswap.com/developer-hub/getting-started/authentication Learn how to obtain and use API keys to authenticate with Houdini ## Overview All API access is managed through the [Houdini Partner Portal](https://app.houdiniswap.com/partner/login). After registration, you will receive: * An API key * An API secret * Access to usage analytics * Commission tracking * Withdrawal management All new accounts are created under the **Free tier**, which includes default rate limits. 1. Sign up using your email address 2. Verify your email 3. Log in to access your dashboard Wallet connection is optional but required for commission withdrawals. After logging in, your dashboard will display: * Your API Key * Your Secret Code Store your secret securely. It will be required for all authenticated requests. If your secret is exposed, contact support to rotate credentials. Sign up to get your API key and secret ## API Base URL All API requests should be made to: ``` https://api-partner.houdiniswap.com/ ``` ## Authentication Method Houdini uses API key and secret authentication passed via the `Authorization` header. ### Header Format Include your API key and secret in every request using this format: ```http theme={null} Authorization: : ``` ### Example Request ```javascript JavaScript theme={null} // Build query parameters const params = new URLSearchParams({ amount: '1', from: 'ETH', to: 'USDC', anonymous: 'true', useXmr: 'false' }); const response = await fetch(`https://api-partner.houdiniswap.com/quote?${params}`, { method: 'GET', headers: { 'Authorization': 'your_api_key:your_api_secret', // Mandatory compliance headers 'x-user-ip': '192.168.1.1', 'x-user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...', 'x-user-timezone': 'America/New_York' } }); const data = await response.json(); ``` ```bash cURL theme={null} curl -X GET "https://api-partner.houdiniswap.com/quote?amount=1&from=ETH&to=USDC&anonymous=true&useXmr=false" \ -H "Authorization: your_api_key:your_api_secret" \ -H "x-user-ip: 192.168.1.1" \ -H "x-user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36..." \ -H "x-user-timezone: America/New_York" ``` ## Mandatory Compliance Headers For compliance purposes, the following **headers** are **required** in all API requests: | Header Name | Description | Example | | ----------------- | -------------------------------- | ------------------------------------------------ | | `x-user-ip` | User's IP address | `"192.168.1.1"` | | `x-user-agent` | User's browser user agent string | `"Mozilla/5.0 (Windows NT 10.0; Win64; x64)..."` | | `x-user-timezone` | User's timezone | `"America/New_York"` | **Compliance Requirement**: These headers are mandatory for AML/KYC compliance. Requests without these headers will be rejected with a 400 error. ## API Documentation For complete API specifications and interactive documentation, refer to: View the full interactive API reference with request/response schemas and examples ## Rate Limits Read more about it on the dedicated Rate limits & Tiers page of the documentation.  ## Security Best Practices **Critical**: Never expose your API key and secret publicly. This API is meant to be used in a **backend environment**, not directly in a frontend UI. * Never commit credentials to version control * Never expose them in client-side JavaScript * Always store them as secure environment variables * Use server-side API calls only ## Next Steps Choose your swap type and start integrating immediately Learn about routing types and order lifecycle # Quick Start Source: https://docs.houdiniswap.com/developer-hub/getting-started/quick-start Jump straight into integrating the swap type you need ## Choose Your Integration Path Houdini offers multiple swap types, each optimized for different use cases. Select the integration guide that matches your needs: **Multi-hop CEX routing for maximum privacy** * 15-45 minute completion * No wallet connection needed * Maximum transaction privacy * Best for: Privacy-focused applications **Fast single-hop CEX swaps** * 3-30 minute completion * No wallet connection needed * Good privacy with speed * Best for: Faster execution needs **Guaranteed output — rate locked at quote time** * 3-30 minute completion * No wallet connection needed * Price certainty regardless of market movement * Best for: High-value swaps, price-sensitive users **On-chain decentralized swaps** * Seconds to minutes * Wallet connection required * Fully transparent on-chain * Best for: DeFi applications **Batch multiple orders in one request** * CEX and anonymous routing * All orders tracked under one `multiId` * Solana batch transaction support * Best for: Payouts, airdrops, bulk distributions ## Integration Overview All swap flows follow a similar pattern: Fetch supported tokens and networks Get swap rates and routing information Submit the swap order Track swap progress to completion All swap types share the same `/quotes` and `/exchanges` endpoints — the `type` field on each quote determines the routing. Follow the detailed guide for your chosen integration path. ## Quick Reference ### Endpoints by Swap Type In API v2, all swap types share the same `/quotes` and `/exchanges` endpoints. Use the `types` parameter to filter which quote types are returned. | Swap Type | Tokens Endpoint | Quote Endpoint | Execute Endpoint | | -------------- | ------------------------ | ------------------------ | --------------------------------------- | | **Standard** | `/v2/tokens?hasCex=true` | `/quotes?types=standard` | `/exchanges` | | **Fixed Rate** | `/v2/tokens?hasCex=true` | `/quotes?fixed=true` | `/exchanges` (requires `refundAddress`) | | **Private** | `/v2/tokens?hasCex=true` | `/quotes?types=private` | `/exchanges` | | **DEX** | `/v2/tokens?hasDex=true` | `/quotes?types=dex` | `/exchanges` | | **Multi-Swap** | `/v2/tokens?hasCex=true` | — | `/exchanges/multi` | Omit `types` to receive all available quote types in a single response. Filter by `types` field on each quote object to select the one you want. ## What You'll Need Before starting any integration: Get your API key and secret from the Houdini team All requests need: * `Authorization: {key}:{secret}` * `x-user-ip: {user_ip}` * `x-user-agent: {user_agent}` * `x-user-timezone: {timezone}` ## Complete Code Examples Ready-to-run Next.js examples for all swap types: Complete, runnable Next.js examples for Private, Standard, and DEX swaps ## Additional Resources Token identifiers and network support Status codes and swap state management Deep dive into each routing strategy Complete API endpoint documentation # Rate Limits & Tiers Source: https://docs.houdiniswap.com/developer-hub/getting-started/rate-limits-and-tiers Understand how API usage limits work and how to upgrade your tier. ## **Tiers** API usage is governed by tier-based, endpoint-specific rate limits. ### Free Tier Default limits apply upon registration. ### Pro Tier Higher default limits apply. For Pro tier partners, limits can be adjusted per agreement. Tier upgrades are handled manually.\ Contact the Houdini team through the [Partner Portal ](https://app.houdiniswap.com/partner/dashboard)to request an upgrade. ## **Endpoint Limits** Rate limits are enforced per endpoint. ### Free Tier Limits | Endpoint | Min | Hour | Day | | :------- | :-- | ---- | --- | | quote | 5 | 20 | 50 | | exchange | 1 | 5 | 10 | ### Pro Tier Limits | Endpoint | Min | Hour | Day | | :------- | :-- | ---- | ---- | | quote | 500 | 2000 | 5000 | | exchange | 50 | 500 | 2000 | ## Rate Limit Enforcement When rate limits are exceeded, the API will throw an error with HTTP status code **429**: \ **Example:** ```json theme={null} { "errors": [ { "message": "Too many quote requests. Try again in 45 seconds.", "extensions": { "code": 429, "type": "RATE_LIMIT_EXCEEDED", "retryAfter": 45, "limit": 45, "windowMs": 60000 } } ] } ``` ```json theme={null} { "code": 429, "type": "RATE_LIMIT_EXCEEDED", "message": "Too many quote requests. Try again in 45 seconds.", "extensions": { "retryAfter": 45, "limit": 45, "windowMs": 60000 } } ``` **Response Fields:** | Field | Description | | ------------ | ---------------------------------------------------- | | `code` | HTTP status code (429) | | `type` | Error type: `RATE_LIMIT_EXCEEDED` | | `message` | Operation-specific message indicating when to retry | | `retryAfter` | Seconds until the rate limit resets | | `limit` | Maximum requests allowed in the time window | | `windowMs` | Time window in milliseconds (e.g., 60000 = 1 minute) | **Best Practice**: Implement exponential backoff when handling rate limit errors. Always respect the `retryAfter` value provided in the response. # Architecture Overview Source: https://docs.houdiniswap.com/developer-hub/overview/architecture Learn how Houdini's intelligent routing engine connects CEXs and on-chain DeFi protocols for optimal swap execution ## System Architecture Houdini acts as an intelligent routing layer that connects users with the best liquidity sources across centralized exchanges (CEXs) and on-chain DeFi protocols (DEXs, bridges, and intent protocols). ## High-Level Flow ```mermaid theme={null} graph LR A[User] --> B[Your Application] B --> C[Houdini API] C --> D{Routing Engine} D -->|Private Route| E[CEX Partners] D -->|On-Chain Route| F[DeFi Protocols] E --> G[Destination Wallet] F --> G ``` ## Key Components ### 1. Your Application (Frontend) Your application integrates with Houdini through: * **REST API**: Direct HTTP API calls for full control * **Widget (iframe)**: Embedded swap interface with minimal integration * Widget SDK coming soon for deeper customization ### 2. Houdini Routing Engine The core intelligence layer that: * Analyzes available routes across CEXs and DEXs * Calculates optimal paths based on amount, fees, and speed * Handles route-specific logic and orchestration * Manages order lifecycle and status updates ### 3. CEX Partners Integrated centralized exchanges that enable: * **Private Swaps**: Multi-hop routing through exchanges for maximum privacy * **Standard Swaps**: Single CEX hop for faster execution * Compliance and AML screening at partner level * Deep liquidity for major trading pairs **Privacy Model**: When using CEX routes, funds briefly touch partner exchanges. Each CEX handles its own AML/KYC requirements. Houdini itself is non-custodial and doesn't hold funds long-term. ### 4. On-Chain DeFi Protocols Direct blockchain integration with: * DEX aggregators and AMMs (Uniswap, etc.) * Cross-chain bridges (Debridge, Mayan, etc.) * Intent-based protocols (Cowswap, Sodax, etc.) ### 5. Destination Wallet Final delivery of swapped assets to: * User-specified wallet address (EOA) * Smart contract addresses (supported, but users must verify correctness) * Any supported blockchain network **Contract Addresses**: While contract addresses are allowed as destinations, users must carefully verify they're sending to the correct contract that can handle the token type. Sending to an incorrect contract may result in permanent loss of funds. ## Routing Strategies Houdini supports multiple routing strategies that can be used independently: Multi-hop CEX routing for maximum privacy. No wallet connection required. Single CEX hop balancing privacy and speed. Transparent on-chain swaps through DEX aggregators. ## Security & Custody Model **Important**: Houdini is an aggregator and affiliate partner, **not a custodial exchange**. * **Non-Custodial**: Houdini doesn't hold user funds long-term * **Partner Routing**: CEX routes involve brief custody by partner exchanges * **Smart Contracts**: DEX routes use audited smart contracts * **No Registration**: Most flows don't require user accounts or KYC * **Partner Compliance**: CEX partners handle their own AML/compliance screening ## Next Steps Set up authentication and get your API credentials Deep dive into routing types and order lifecycle # What You Can Build Source: https://docs.houdiniswap.com/developer-hub/overview/what-you-can-build Explore the possibilities with Houdini's cross-chain swap infrastructure ## Build Privacy-First Swap Solutions Houdini provides a comprehensive API and widget infrastructure for building cross-chain swap applications with varying levels of privacy and user experience. ## Core Use Cases Execute fully private cross-chain swaps without requiring wallet connections. Perfect for users who prioritize privacy and anonymity. Enable efficient swaps with a single CEX hop, balancing privacy with speed and liquidity. Lock the exchange rate at quote time for guaranteed output — ideal for partners who need price certainty. Available for standard (CEX) swaps only. Access top DEXs and bridges for transparent on-chain swaps with competitive rates. Create multiple swaps in a single request — ideal for payout distribution, batch airdrops, or any use case requiring simultaneous swaps to multiple recipients. Supports CEX and anonymous routing. Create shareable payment links for P2P transactions or merchant payment flows with built-in privacy. ## Integration Options ### Direct API Integration Build custom swap experiences by integrating directly with Houdini's APIs: * **REST API**: HTTP endpoints for full control over the swap experience * Full control over user experience and flow * Custom branding and UI design * Advanced routing and configuration options ### Drop-in Widget Embed a fully functional swap interface into your application with a single iframe: * Pre-built UI, configurable via URL parameters (theming coming soon) * Works on any website or web application * Minimal integration effort * Maintained and updated by Houdini See the [Widget Overview](/developer-hub/widget/overview) for the embed snippet and configuration options. ## Key Features * **Multi-Route Support**: Choose between private (CEX-based), semi-private, pure DEX routing, or fixed rate standard swaps * **Best Price Execution**: Automatic routing to find optimal rates across all supported venues * **Privacy Options**: Varying levels of privacy to match your users' needs * **Multi-Swap**: Create and track multiple swap orders in one API call, with Solana batch transaction support * **Comprehensive Coverage**: Support for 100+ chains * **Real-time Updates**: Track swap status via REST polling or WebSocket push * **Production Ready**: Battle-tested infrastructure with enterprise-grade reliability ## Next Steps Understand how Houdini's routing engine works Obtain API keys and make your first swap # DEX Swap Integration Source: https://docs.houdiniswap.com/developer-hub/swap-flows/dex-swap 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. **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. Looking for the v1 DEX swap guide? See [API v1 — DEX Swap](/api-v1/swap-flows/dex-swap). 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). ### Supported Networks * **EVM** (Ethereum, BSC, Polygon, etc.) * **Solana** * **SUI** * **TRON** * **TON** * **Stellar** **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. ## 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 Bulk fetch DEX-supported tokens and cache to your DB, or search by name/symbol. Note each token's `id`. Call `GET /quotes` with token IDs and optional slippage. Select the quote with `type: "dex"`. 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`. Broadcast approval transactions via user's wallet. Have user sign EIP-712 typed data. For chained signatures, call `POST /dex/chainSignatures` until `isComplete: true`. Call `POST /exchanges` with `quoteId`, `addressTo`, `addressFrom`, and any collected signatures. If not off-chain, have user broadcast the transaction. Then call `POST /dex/confirmTx` with the transaction hash. Poll `GET /orders/{houdiniId}` until `statusLabel` is `FINISHED`. ## Integration Guide ### Step 1: Get Tokens There are two approaches for getting tokens. Choose the one that fits your integration: Fetch all DEX-supported tokens once and store them in your backend database. Cache the token list in your backend database. Never call `/tokens` on every user request — refresh every 24 hours. ```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" ``` Search for specific tokens by name or symbol on demand. ```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" ``` ### Step 2: Get DEX Quote Call `GET /quotes` with token IDs and optional `slippage`. Select the quote with `type: "dex"`. ```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" ``` ### 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 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). Call `POST /dex/approve` with just the `quoteId` and user's wallet address: ```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" }' ``` **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) You may receive both `approvals` and `signatures`. Handle approvals first, then signatures. #### 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) ```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); } } ``` Skip this step if `approvals` is empty. ### Step 5: Process Signatures (if needed) ```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); ``` **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` Skip this step if `signatures` is empty. ### Step 6: Create Order Call `POST /exchanges` with the `quoteId`, addresses, and any collected signatures: ```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": [] }' ``` **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 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 }) }); } ``` 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 }) }); } ``` 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. ### 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: ```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" ``` 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). ## Example Repositories See full working integrations on GitHub: Full Next.js integration showing standard, private, and DEX swap flows Backend Node.js integration with token fetching, quoting, and order tracking ## Next Steps Fast single-hop CEX swaps Multi-hop privacy swaps Understand all order statuses Handle errors and edge cases # Fixed Rate Swap Source: https://docs.houdiniswap.com/developer-hub/swap-flows/fixed-rate-swap Lock the exchange rate at quote time — guaranteed output amount regardless of market movement ## Overview Fixed rate swaps lock the exchange rate at the moment you request a quote. The user receives exactly the quoted `amountOut` regardless of market movement between quote time and swap execution — as long as the exchange is created before the rate lock expires. **Best For**: Partners and users who want price certainty and are willing to plan around a short rate-lock window. Ideal for high-value swaps where slippage risk is unacceptable. Fixed rate is only available for **standard (CEX) swaps**. It cannot be combined with private/anonymous swaps or DEX swaps. Attempting either will return a 422 error. ## Key Features User receives exactly `amountOut` as quoted — no slippage, no surprises at settlement `validUntil` timestamp gives you a window to create the exchange before the rate expires If the swap fails after the rate is locked, the `refundAddress` is used for any potential refund — though refund handling depends on the provider The rate is held by a specific CEX provider via `rateId` — no silent fallback to a different provider or rate ## How It Works Fetch CEX-supported tokens and cache to your DB. Note each token's `id`. Call `GET /quotes` with `fixed=true`. The response includes a `quoteId`, guaranteed `amountOut`, and `validUntil` expiry. Call `POST /exchanges` with the `quoteId`, `addressTo`, and a `refundAddress`. The `refundAddress` is required for fixed rate orders. Send exactly `inAmount` of the input token to the `depositAddress` returned in the order. Poll `GET /orders/{houdiniId}` until `statusLabel` is `FINISHED`. If the swap fails, status may become `REFUNDED` — refund handling depends on the provider. ## Integration Guide ### Step 1: Get Tokens Token fetching is the same as for standard swaps — paginate through `/v2/tokens?hasCex=true` and cache the results. See [Standard Swap — Step 1](/developer-hub/swap-flows/standard-swap#step-1-get-tokens) for the full code example. Use each token's `id` field (not `symbol`) in the `/quotes` request. ### Step 2: Get a Fixed Rate Quote Add `fixed=true` to your `/quotes` request. The API will automatically restrict results to standard (CEX) quotes only — you do not need to pass `types=standard` separately. Pass `refundAddress` at quote time so it is validated early. It must be a valid address on the **source chain**. ```javascript JavaScript theme={null} const params = new URLSearchParams({ amount: '1', from: '6689b73ec90e45f3b3e51566', // ETH token id from /tokens to: '6689b73ec90e45f3b3e51558', // USDC token id from /tokens fixed: 'true', // request fixed-rate quotes refundAddress: '0xYourRefundWallet', // source-chain wallet for refunds }); 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(); // All returned quotes will be fixed-rate standard quotes const quote = quotes[0]; console.log('Guaranteed out:', quote.amountOut); console.log('Rate locked until:', quote.validUntil); console.log('Quote ID:', quote.quoteId); // needed for /exchanges ``` ```bash cURL theme={null} curl -X GET "https://api-partner.houdiniswap.com/v2/quotes?amount=1&from=6689b73ec90e45f3b3e51566&to=6689b73ec90e45f3b3e51558&fixed=true&refundAddress=0xYourRefundWallet" \ -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" ``` ### Fixed Rate Quote Response ```json theme={null} { "quotes": [ { "quoteId": "69af9e02f9c5affabcaccc14", "type": "standard", "swap": "cc", "swapName": "Coincraddle", "logoUrl": "https://api.houdiniswap.com/assets/logos/coincraddle.jpg", "amountIn": 1, "amountOut": 2456.78, "amountOutUsd": 2456.78, "min": 0.098169, "max": 4688.581529, "duration": 30, "fixed": true, "rateId": "abc123providerratelockid", "validUntil": "2025-12-25T06:28:00.000Z", "rewardsAvailable": true } ], "total": 3 } ``` **Key fixed-rate quote fields:** | Field | Type | Description | | ------------ | ----------------- | ---------------------------------------------------------------------------- | | `quoteId` | string | Pass to `/exchanges` to create the order | | `fixed` | boolean | `true` confirms this is a fixed-rate quote | | `rateId` | string | Provider-internal rate lock identifier — carried automatically via `quoteId` | | `validUntil` | string (ISO 8601) | Hard deadline — create the exchange before this timestamp | | `amountOut` | number | Guaranteed output amount | `validUntil` is a hard deadline with no grace period. Check `Date.now() < new Date(quote.validUntil)` before calling `/exchanges`. If you miss the window, re-fetch a new quote. ### Step 3: Create the Exchange Pass `quoteId`, `addressTo`, and `refundAddress` to `POST /exchanges`. The `refundAddress` is **required** for fixed rate orders — omitting it returns a 422 error. ```javascript JavaScript theme={null} // Always check the rate is still valid before submitting if (Date.now() >= new Date(quote.validUntil).getTime()) { throw new Error('Rate expired — fetch a new quote'); } 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: '69af9e02f9c5affabcaccc14', // from /quotes addressTo: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', refundAddress: '0xYourRefundWallet' // required for fixed rate }) }); const order = await response.json(); console.log('Order ID:', order.houdiniId); console.log('Deposit to:', order.depositAddress); console.log('Send:', order.inAmount, order.inSymbol); console.log('Rate locked:', order.fixed, '— guaranteed out:', order.outAmount); ``` ```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": "69af9e02f9c5affabcaccc14", "addressTo": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "refundAddress": "0xYourRefundWallet" }' ``` ### Exchange Response ```json theme={null} { "houdiniId": "iBQMRX3xvXrFMGQi71ogo9", "created": "2025-12-25T06:13:46.673Z", "expires": "2025-12-25T06:43:46.673Z", "depositAddress": "0x7364a0b6c55004427a4a7c26355ce9c75ef56194", "receiverAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "refundAddress": "0xYourRefundWallet", "fixed": true, "validUntil": "2025-12-25T06:28:00.000Z", "anonymous": false, "status": -1, "statusLabel": "NEW", "inAmount": 1, "inSymbol": "ETH", "outAmount": 2456.78, "outSymbol": "USDC", "eta": 3, "swapName": "Coincraddle" } ``` **Key response fields:** | Field | Type | Description | | ----------------------- | ----------------- | ----------------------------------------------------------------------------------------------- | | `houdiniId` | string | Unique order ID — use for status polling | | `depositAddress` | string | Send input funds here | | `inAmount` / `inSymbol` | number / string | Exact amount and token to send | | `expires` | string (ISO 8601) | Deposit deadline (typically 30 minutes) | | `fixed` | boolean | Confirms the order is rate-locked | | `validUntil` | string (ISO 8601) | Rate lock expiry echoed from the quote | | `refundAddress` | string | Address used for refunds if the swap fails — whether a refund is issued depends on the provider | The rate is bound to a single provider via the `rateId` embedded in the `quoteId`. If that provider loses fixed-rate capability between quote and exchange time, you will receive `FIXED_RATE_QUOTE_EXPIRED` — there is no silent fallback to a different provider or rate. Send exactly `inAmount` of `inSymbol` to `depositAddress` before `expires`. ### Step 4: Send Deposit After creating the order, send the input tokens to `depositAddress`. The status advances automatically once the deposit is detected on-chain. Send funds before the `expires` deadline. If funds arrive after the deadline, the fixed rate is no longer guaranteed and the order may not be processed. Please [contact our customer support](/faqs/contact-support) for assistance. ### Step 5: 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: ```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 deposit // CONFIRMING — deposit detected, awaiting confirmations // EXCHANGING — CEX processing the swap at locked rate // FINISHED — swap complete, guaranteed amount sent // FAILED — swap failed // REFUNDED — swap failed; refund handling depends on provider ``` ```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" ``` ### Status Progression ``` NEW / WAITING ↓ Deposit sent and detected CONFIRMING ↓ Blockchain confirmations received EXCHANGING ↓ CEX processes the swap at the locked rate FINISHED ← guaranteed amountOut delivered (or) REFUNDED ← swap failed; refund handling depends on provider ``` Poll every 30 seconds. Fixed rate swaps follow the same timeline as standard swaps (typically 3–30 minutes). For real-time updates without polling, use the [WebSocket API](/developer-hub/core-concepts/websocket-order-updates). *** ## Error Codes | Code | HTTP | When | | ---------------------------------------- | ---- | ---------------------------------------------------------------------------------------------- | | `REFUND_ADDRESS_REQUIRED_FOR_FIXED_RATE` | 422 | `/exchanges` called without `refundAddress` | | `ANONYMOUS_AND_FIXED_RATE_NOT_SUPPORTED` | 422 | `fixed=true` combined with private/anonymous swap mode | | `DEX_AND_FIXED_RATE_NOT_SUPPORTED` | 422 | `fixed=true` combined with a DEX quote type | | `FIXED_RATE_QUOTE_EXPIRED` | 422 | `validUntil` has passed, or the provider lost fixed-rate capability after the quote was issued | | `ADDRESS_INVALID_FOR_CHAIN` | 422 | `refundAddress` fails format validation for the source chain | *** ## Best Practices Build in a buffer — don't wait until the last second. Check `Date.now() < new Date(quote.validUntil)` immediately before calling `/exchanges`. If the window is too short, fetch a fresh quote rather than risk `FIXED_RATE_QUOTE_EXPIRED`. The refund wallet is validated against source-chain address format rules (including Solana, XRP, and other non-EVM chains). Pass it at quote time so validation happens early — not when you create the exchange. Fixed rate is tied to one provider. If that provider disables fixed rate or goes down between your quote and exchange calls, the order fails with `FIXED_RATE_QUOTE_EXPIRED`. Re-quoting will select a new provider if one is available. Display `amountOut` and `validUntil` prominently in your UI before the user confirms. This sets clear expectations about the guaranteed output and how long they have to act. *** ## Common Issues **Cause**: The exchange was created after `validUntil`, or the provider lost fixed-rate capability between quote and exchange time. **Solution**: Fetch a new quote. If the error recurs, the provider may have temporarily disabled fixed rate — try again after a short delay or present the user with a floating-rate quote as a fallback. **Cause**: `refundAddress` was not included in the `/exchanges` request body. **Solution**: Always include `refundAddress` when creating a fixed rate exchange. Collect and validate the address before the user submits. **Cause**: The swap failed after the rate lock was accepted — for example, the CEX rejected the transaction. **Solution**: Refund handling depends on the provider. Some providers automatically return funds to `refundAddress`; others require the user to contact customer support. Inform the user of the failure and direct them to support if a refund is not issued automatically. *** ## Next Steps Fast single-hop CEX swaps without rate lock Multi-hop privacy swaps via CEX routing Understand all order statuses Handle errors and edge cases # Multi-Swap Flow Source: https://docs.houdiniswap.com/developer-hub/swap-flows/multi-swap Create and manage multiple swaps in a single batch — supports CEX and anonymous routing ## Overview Multi-swap lets partners create multiple independent swap orders in a single API call, grouped under a shared `multiId`. Each order is individually priced and executed, but they can all be tracked together via a single status endpoint. **Best For**: Platforms distributing payouts to multiple recipients, batch airdrop tools, or any use case requiring several simultaneous swaps from the same source token. Multi-swap supports **CEX and anonymous routing only**. DEX orders and fixed rate are not currently supported. Each order in the batch is created using the standard `from`/`to`/`amount`/`addressTo` fields — no `quoteId` is needed. ## Key Features Create up to many orders in one request, all linked by a shared `multiId` Supports standard (CEX) and private (anonymous) routing per order Track all orders in the group with a single `GET /exchanges/multi/{multiId}` call Fund all deposit addresses in the group with a single signed transaction via `GET /exchanges/multi/{multiId}/tx`. Up to 10 orders per transaction. ## How It Works Fetch CEX-supported tokens and note each token's `id`. Call `POST /exchanges/multi` with an array of orders. Each order specifies token IDs, amount, destination address, and optional routing flags. Poll `GET /exchanges/multi/{multiId}` to track all orders in the group together. For Solana source tokens, call `GET /exchanges/multi/{multiId}/tx?sender={senderAddress}` to get pre-built batched transactions. Each transaction covers up to 10 deposit addresses — if your batch has more than 10 orders, the response returns multiple transactions to submit separately. ## Integration Guide ### Step 1: Get Tokens Use token IDs (not symbols) when building your orders. Fetch and cache the token list from `/tokens`: Cache the token list in your backend database. Never call `/tokens` on every user request — load on server startup or via a scheduled job and refresh every 24 hours. ```javascript JavaScript theme={null} async function fetchAllCexTokens() { let page = 1; let allTokens = []; while (true) { const response = await fetch( `https://api-partner.houdiniswap.com/v2/tokens?hasCex=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?hasCex=true&pageSize=20&page=1" \ -H "Authorization: your_api_key:your_api_secret" ``` ```javascript JavaScript theme={null} async function searchTokens(query) { const params = new URLSearchParams({ term: query, hasCex: '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; } ``` ```bash cURL theme={null} curl -X GET "https://api-partner.houdiniswap.com/v2/tokens?term=solana&hasCex=true&pageSize=20&page=1" \ -H "Authorization: your_api_key:your_api_secret" ``` ### Step 2: Create Multi-Swap Call `POST /exchanges/multi` with an `orders` array. Each order is independent — different token pairs, amounts, and destination addresses are all supported in the same batch. Set `anonymous: true` on any order to enable private routing for that order. ```javascript JavaScript theme={null} const response = await fetch('https://api-partner.houdiniswap.com/v2/exchanges/multi', { method: 'POST', headers: { 'Authorization': `${API_KEY}:${API_SECRET}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ orders: [ { from: '6689b73ec90e45f3b3e51577', // SOL token id to: '6689b73ec90e45f3b3e51566', // ETH token id amount: 10, addressTo: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' }, { from: '6689b73ec90e45f3b3e51577', // SOL token id to: '6689b73ec90e45f3b3e51558', // USDC token id amount: 25, addressTo: '0xABcD1234abcd1234ABCD1234abcd1234ABCD1234', anonymous: true // enable private routing for this order }, { from: '6689b73ec90e45f3b3e51577', // SOL token id to: '6689b73ec90e45f3b3e51577', // SOL token id amount: 5, addressTo: 'RecipientSolanaAddressHere1111111111111111', anonymous: true, useXmr: true // force XMR as anonymous bridge } ] }) }); const result = await response.json(); console.log('Multi ID:', result.multiId); // shared group identifier result.orders.forEach((item, i) => { if (item.error) { console.error(`Order ${i} failed:`, item.error.message, item.error.code); } else { console.log(`Order ${i} ID:`, item.order.houdiniId); console.log(` Deposit to:`, item.order.depositAddress); console.log(` Send:`, item.order.inAmount, item.order.inSymbol); console.log(` Expires:`, item.order.expires); } }); ``` ```bash cURL theme={null} curl -X POST "https://api-partner.houdiniswap.com/v2/exchanges/multi" \ -H "Authorization: your_api_key:your_api_secret" \ -H "Content-Type: application/json" \ -d '{ "orders": [ { "from": "6689b73ec90e45f3b3e51577", "to": "6689b73ec90e45f3b3e51566", "amount": 10, "addressTo": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e" }, { "from": "6689b73ec90e45f3b3e51577", "to": "6689b73ec90e45f3b3e51558", "amount": 25, "addressTo": "0xABcD1234abcd1234ABCD1234abcd1234ABCD1234", "anonymous": true } ] }' ``` ### Create Multi-Swap Response ```json theme={null} { "multiId": "multi_a1b2c3d4e5f6", "orders": [ { "order": { "houdiniId": "iBQMRX3xvXrFMGQi71ogo9", "multiId": "multi_a1b2c3d4e5f6", "created": "2025-12-25T06:13:46.673Z", "expires": "2025-12-25T06:43:46.673Z", "depositAddress": "SolDepositAddress111111111111111111111111111", "receiverAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "anonymous": false, "statusLabel": "NEW", "inAmount": 10, "inSymbol": "SOL", "outAmount": 0.0412, "outSymbol": "ETH", "eta": 15 } }, { "order": { "houdiniId": "kJRNSY4ywYsGNHRmO82ph0", "multiId": "multi_a1b2c3d4e5f6", "created": "2025-12-25T06:13:46.673Z", "expires": "2025-12-25T06:43:46.673Z", "depositAddress": "SolDepositAddress222222222222222222222222222", "receiverAddress": "0xABcD1234abcd1234ABCD1234abcd1234ABCD1234", "anonymous": true, "statusLabel": "NEW", "inAmount": 25, "inSymbol": "SOL", "outAmount": 2431.75, "outSymbol": "USDC", "eta": 35 } }, { "error": { "message": "Token pair not available", "code": "PAIR_UNAVAILABLE" } } ] } ``` **Key Response Fields:** * `multiId`: Shared identifier for the batch — use this to poll status for all orders * `orders[].order`: Full order object for successfully created orders (see [Order Lifecycle](/developer-hub/core-concepts/order-lifecycle)) * `orders[].error`: Per-order error if creation failed — the rest of the batch is unaffected * `depositAddress`: Each order has its own unique deposit address * `statusLabel`: Starts as `NEW` — orders are initialized asynchronously Orders are created asynchronously. Some orders may return with `statusLabel: "INITIALIZING"` immediately after creation. Poll the multi status endpoint to confirm all orders reach `NEW` before proceeding. ### Order Fields Reference | Field | Description | | ---------------- | ------------------------------------------------------------------ | | `from` | Token ID of input token (24-char MongoDB ObjectId) | | `to` | Token ID of output token | | `amount` | Input swap amount | | `addressTo` | Destination wallet address for output funds | | `anonymous` | `true` to enable private multi-hop routing | | `destinationTag` | Memo/tag for chains that require it (e.g. XRP, XLM) — max 64 chars | | `useXmr` | `true` to force XMR as the anonymous bridge layer | | `walletInfo` | Additional wallet metadata — max 256 chars | ### Step 3: Monitor Multi-Swap Status Poll `GET /exchanges/multi/{multiId}` to retrieve status for all orders in the group at once: ```javascript JavaScript theme={null} async function pollMultiStatus(multiId) { const response = await fetch( `https://api-partner.houdiniswap.com/v2/exchanges/multi/${multiId}`, { headers: { 'Authorization': `${API_KEY}:${API_SECRET}` } } ); const { orders } = await response.json(); orders.forEach(order => { console.log(`${order.houdiniId}: ${order.statusLabel}`); // statusLabel values: // INITIALIZING — order being set up // NEW / WAITING — awaiting deposit // CONFIRMING — deposit detected, awaiting confirmations // EXCHANGING — CEX is processing // ANONYMIZING — routing through privacy layer (anonymous orders only) // FINISHED — complete, funds sent // EXPIRED — deposit not received in time // FAILED — swap failed // REFUNDED — funds returned to sender }); const allDone = orders.every(o => ['FINISHED', 'FAILED', 'EXPIRED', 'REFUNDED'].includes(o.statusLabel) ); return { orders, allDone }; } // Poll every 30 seconds until all orders are terminal const interval = setInterval(async () => { const { orders, allDone } = await pollMultiStatus('multi_a1b2c3d4e5f6'); if (allDone) clearInterval(interval); }, 30_000); ``` ```bash cURL theme={null} curl -X GET "https://api-partner.houdiniswap.com/v2/exchanges/multi/multi_a1b2c3d4e5f6" \ -H "Authorization: your_api_key:your_api_secret" ``` ### Multi-Status Response ```json theme={null} { "multiId": "multi_a1b2c3d4e5f6", "orders": [ { "houdiniId": "iBQMRX3xvXrFMGQi71ogo9", "multiId": "multi_a1b2c3d4e5f6", "statusLabel": "WAITING", "displayStatus": "WAITING_FOR_DEPOSIT", "inAmount": 10, "inSymbol": "SOL", "outAmount": 0.0412, "outSymbol": "ETH", "depositAddress": "SolDepositAddress111111111111111111111111111", "receiverAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "eta": 15, "created": "2025-12-25T06:13:46.673Z", "modified": "2025-12-25T06:13:50.001Z" }, { "houdiniId": "kJRNSY4ywYsGNHRmO82ph0", "multiId": "multi_a1b2c3d4e5f6", "statusLabel": "EXCHANGING", "displayStatus": "DEPOSIT_DETECTED", "inAmount": 25, "inSymbol": "SOL", "outAmount": 2431.75, "outSymbol": "USDC", "depositAddress": "SolDepositAddress222222222222222222222222222", "receiverAddress": "0xABcD1234abcd1234ABCD1234abcd1234ABCD1234", "eta": 20, "created": "2025-12-25T06:13:46.673Z", "modified": "2025-12-25T06:20:12.443Z" } ] } ``` Poll every 30 seconds. Track each order's `statusLabel` independently — orders in the same batch progress at different rates. For a full list of statuses and transitions, see the [Order Lifecycle](/developer-hub/core-concepts/order-lifecycle) guide. ### Step 4: Generate Solana Batch Transaction For Solana source tokens, you can fund all deposit addresses with pre-built transactions instead of sending separate transfers. Call `GET /exchanges/multi/{multiId}/tx?sender={senderAddress}`: ```javascript JavaScript theme={null} const senderAddress = 'YourSolanaWalletAddressHere111111111111111111'; const response = await fetch( `https://api-partner.houdiniswap.com/v2/exchanges/multi/${multiId}/tx?sender=${senderAddress}`, { headers: { 'Authorization': `${API_KEY}:${API_SECRET}` } } ); const { chain, transactions } = await response.json(); console.log('Chain:', chain); // "solana" console.log('Transactions to submit:', transactions.length); for (const batch of transactions) { console.log('Covers order IDs:', batch.houdiniIds); // up to 10 per batch // batch.txData.data is a base64-encoded serialized Solana transaction const txBytes = Buffer.from(batch.txData.data, 'base64'); const { Transaction, Connection, clusterApiUrl } = require('@solana/web3.js'); const connection = new Connection(clusterApiUrl('mainnet-beta')); const tx = Transaction.from(txBytes); // Sign with your keypair or wallet adapter, then submit const signature = await connection.sendRawTransaction(tx.serialize()); console.log('Submitted batch tx:', signature); } ``` ```bash cURL theme={null} curl -X GET "https://api-partner.houdiniswap.com/v2/exchanges/multi/multi_a1b2c3d4e5f6/tx?sender=YourSolanaWalletAddressHere111111111111111111" \ -H "Authorization: your_api_key:your_api_secret" ``` ### Batch Transaction Response ```json theme={null} { "multiId": "multi_a1b2c3d4e5f6", "chain": "solana", "transactions": [ { "houdiniIds": [ "iBQMRX3xvXrFMGQi71ogo9", "kJRNSY4ywYsGNHRmO82ph0" ], "txData": { "data": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEDa2V5..." } } ] } ``` **Key Fields:** * `chain`: Always `"solana"` — only Solana is supported for batch transactions * `transactions`: Array of `TxBatch` objects — each covers up to 10 orders. Batches with more than 10 orders return multiple items; submit each transaction separately * `houdiniIds`: The order IDs funded by this specific transaction * `txData.data`: Base64-encoded serialized Solana transaction — deserialize, sign, and submit Batch transactions are only supported when all orders in the multi-swap share the same **Solana** source token. ## Best Practices The multi-swap endpoint returns a partial success response — some orders may fail while others succeed. Always check each item in `orders[]` for an `error` field before proceeding, and handle failed orders independently without canceling the batch. Orders returned with `statusLabel: "INITIALIZING"` are still being set up. Poll `GET /exchanges/multi/{multiId}` until all orders reach `NEW` or `WAITING` before sending deposits. Each order has its own `expires` timestamp (typically 30 minutes from creation). For Solana batch transactions, sign and submit all transactions promptly — if any order expires before confirmation, that order will not be funded. If your multi-swap has more than 10 Solana orders, the `transactions` array will contain multiple items. Submit each transaction independently — they are not dependent on each other and can be submitted in parallel. Never expose API keys in frontend code. Validate all `addressTo` values before submission. Store `multiId` and each `houdiniId` for audit trails and support lookups. ## Common Issues **Cause**: Individual orders can fail validation (unsupported pair, amount out of range, invalid address) while the rest of the batch succeeds. **Solution**: Check each `orders[].error` in the response. Re-submit failed orders individually with corrected parameters. The valid orders in the batch are unaffected. **Cause**: Batch transactions require all orders to share the same Solana source token. **Solution**: Use separate individual deposits for non-Solana source tokens. **Cause**: The 30-minute deposit window elapsed before funds were sent. **Solution**: Re-create the expired orders with a new multi-swap request. Deposit promptly after creation. **Cause**: Order setup is asynchronous and may take a few seconds. **Solution**: Poll `GET /exchanges/multi/{multiId}` every 5–10 seconds until all orders leave the `INITIALIZING` state before proceeding. **Cause**: Private routing passes through an additional `ANONYMIZING` stage. **Solution**: This is expected. Monitor `statusLabel` per order — anonymous orders typically complete in 15–45 minutes. ## Next Steps Single-order CEX swaps Multi-hop anonymous swaps Understand all order statuses Handle errors and edge cases # Private Swap Integration Source: https://docs.houdiniswap.com/developer-hub/swap-flows/private-swap Integrate multi-hop private swaps using the unified v2 quotes and exchanges API ## Overview Private swaps route through **multiple CEX hops** to break the transaction trail, providing enhanced anonymity. Optionally routes through Monero (XMR) as an untraceable intermediate layer. In API v2, private swaps use the same `/quotes` and `/exchanges` endpoints as standard and DEX swaps — pass `types=private` to get only private quotes, or filter the response by `type: "private"`. **Best For**: Users who prioritize privacy and are willing to accept longer completion times (15–45 minutes) for enhanced anonymity. Looking for the v1 private swap guide? See [API v1 — Private Swap](/api-v1/swap-flows/private-swap). Fixed rate is not available for private swaps. To guarantee the output amount, use a [Fixed Rate Standard Swap](/developer-hub/swap-flows/fixed-rate-swap) instead. ## Key Features Routes through 2 exchanges to break the transaction trail Monero used as an untraceable intermediate when available No browser wallet or on-chain approvals required No direct on-chain link between source and destination ## How It Works Bulk fetch CEX-supported tokens and cache to your DB, or search by name/symbol. Note each token's `id`. Call `GET /quotes` with token IDs. Either pass `types=private` to get only private quotes, or filter the response by `type: "private"`. Call `POST /exchanges` with the selected `quoteId` and destination address. Send exactly `inAmount` to the `depositAddress` returned in the order. Poll `GET /orders/{houdiniId}`. Private swaps pass through `ANONYMIZING` before `FINISHED`. ## Integration Guide ### Step 1: Get Tokens There are two approaches for getting tokens. Choose the one that fits your integration: Fetch all CEX-supported tokens once and store them in your backend database. Cache the token list in your backend database. Never call `/tokens` on every user request — load on server startup or via a scheduled job and refresh every 24 hours. ```javascript JavaScript theme={null} async function fetchAllCexTokens() { let page = 1; let allTokens = []; while (true) { const response = await fetch( `https://api-partner.houdiniswap.com/v2/tokens?hasCex=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?hasCex=true&pageSize=20&page=1" \ -H "Authorization: your_api_key:your_api_secret" ``` Search for specific tokens by name or symbol on demand. ```javascript JavaScript theme={null} async function searchTokens(query) { const params = new URLSearchParams({ term: query, hasCex: '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=monero&hasCex=true&pageSize=20&page=1" \ -H "Authorization: your_api_key:your_api_secret" ``` ### Step 2: Get Private Quote Call `GET /quotes` with token IDs. Pass `types=private` to receive only private quotes, or omit it to get all types and filter by `type: "private"`. ```javascript JavaScript theme={null} const params = new URLSearchParams({ amount: '1', from: '6689b73ec90e45f3b3e51566', // ETH token id to: '6689b73ec90e45f3b3e51577', // SOL token id types: 'private', // only return private quotes }); 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 private (multi-hop) quote const privateQuote = quotes.find(q => q.type === 'private'); console.log('Amount out:', privateQuote.amountOut); console.log('ETA:', privateQuote.duration, 'minutes'); // typically 60 min console.log('Quote ID:', privateQuote.quoteId); // needed for /exchanges ``` ```bash cURL theme={null} curl -X GET "https://api-partner.houdiniswap.com/v2/quotes?amount=1&from=6689b73ec90e45f3b3e51566&to=6689b73ec90e45f3b3e51577&types=private" \ -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" ``` ### Quotes Response (private quote) ```json theme={null} { "quotes": [ { "quoteId": "69af93b1f9c5affabcaccbdb", "type": "private", "amountIn": 1, "amountOut": 25842.84927999, "amountOutUsd": 2007.5759034667433, "min": 0.09828, "max": 4228.846045, "duration": 60, "rewardsAvailable": true } ], "total": 5 } ``` **Key Private Quote Fields:** * `quoteId`: Pass this to `/exchanges` to create the order * `type`: `"private"` indicates multi-hop routing * `duration`: Estimated time in minutes — longer due to multi-hop (typically 60 min) ### Step 3: Create Private Order Pass the `quoteId` and destination address to `POST /exchanges`. No additional parameters are needed to enable private routing — the quote type determines the routing. ```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: '694cd4b5d924391f000561e3', // private quote from /quotes addressTo: '1nc1nerator11111111111111111111111111111111' }) }); const order = await response.json(); console.log('Order ID:', order.houdiniId); console.log('Deposit to:', order.depositAddress); console.log('Send amount:', order.inAmount, order.inSymbol); console.log('Expires:', order.expires); ``` ```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": "694cd4b5d924391f000561e3", "addressTo": "1nc1nerator11111111111111111111111111111111" }' ``` ### Order Response ```json theme={null} { "houdiniId": "bwc5iKVeeW5GiQpLHCm65w", "created": "2025-12-25T06:13:21.293Z", "expires": "2025-12-25T06:43:21.293Z", "depositAddress": "0xA2fC2BD472aB6FAF3176EBcBCaeeC7f95F563Ada", "receiverAddress": "1nc1nerator11111111111111111111111111111111", "anonymous": true, "status": -1, "statusLabel": "NEW", "inAmount": 1, "inSymbol": "ETH", "inStatus": 0, "inStatusLabel": "NEW", "outAmount": 23.66258493, "outSymbol": "SOL", "outStatus": 0, "outStatusLabel": "NEW", "eta": 28, "swapName": "Changelly → Quickex" } ``` Send exactly `inAmount` of `inSymbol` to `depositAddress` before `expires` (typically 30 minutes). ### Step 4: Monitor Order Status Poll `GET /orders/{houdiniId}` to track multi-hop progress, or subscribe via the [WebSocket API](/developer-hub/core-concepts/websocket-order-updates) for real-time updates: ```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('Overall status:', status.statusLabel); console.log('First hop:', status.inStatusLabel); // input leg console.log('Second hop:', status.outStatusLabel); // output leg ``` ```bash cURL theme={null} curl -X GET "https://api-partner.houdiniswap.com/v2/orders/bwc5iKVeeW5GiQpLHCm65w" \ -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" ``` ### Private Swap Status Progression Private swaps pass through an additional `ANONYMIZING` stage during the XMR privacy layer: ``` NEW / WAITING ↓ Deposit sent and detected CONFIRMING ↓ Blockchain confirmations received EXCHANGING ↓ First CEX hop processing ANONYMIZING ↓ Routing through XMR privacy layer ↓ Second CEX hop processing FINISHED ``` **Status Fields:** * `statusLabel`: Overall order status * `inStatusLabel`: First hop status * `outStatusLabel`: Second hop status (private swaps only) Poll every 30 seconds. Private swaps typically complete in 15–45 minutes due to multi-hop routing. For real-time updates without polling, use the [WebSocket API](/developer-hub/core-concepts/websocket-order-updates). ## Best Practices Clearly communicate the 15–45 minute completion time. Users should understand they are trading speed for privacy. Display both `inStatusLabel` and `outStatusLabel` to show users which leg of the swap is processing. Implement loading states and progress indicators. Multi-hop routing takes significantly longer than standard swaps. Never expose API keys in frontend code. Validate all destination addresses before submitting. Store `houdiniId` for support lookups. ## Common Issues **Cause**: Multi-hop routing through 2 exchanges takes longer than a direct swap. **Solution**: This is expected for private swaps. Monitor `inStatusLabel` and `outStatusLabel` to see which hop is processing. **Question**: "How private is this really?" **Answer**: Private swaps break the transaction trail by routing through multiple exchanges. When XMR routing is used, it adds an untraceable intermediate step. However, this is not absolute anonymity — compliance checks still apply. **Cause**: Deposit was not received before the `expires` timestamp. **Solution**: Fetch a new quote and create a new order. ## Example Repositories See full working integrations on GitHub: Full Next.js integration showing standard, private, and DEX swap flows Backend Node.js integration with token fetching, quoting, and order tracking ## Next Steps Fast single-hop CEX swaps On-chain decentralized swaps Understand all order statuses # Private Send Integration Source: https://docs.houdiniswap.com/developer-hub/swap-flows/send Privately send a token to another address and receive the same asset out, using the v2 quotes and exchanges API ## Overview A Private Send is a [Private Swap](/developer-hub/swap-flows/private-swap) where the **input and output token are the same**. Instead of swapping into a different asset, the user moves a token to a new destination address and receives the **same asset** back out (for example, USDT in → USDT out). Everything else — endpoints, `types=private` multi-hop routing, the optional XMR privacy layer, status progression, and completion times — is identical to a Private Swap. **The only difference is that `from` and `to` reference the same token.** **Best For**: Moving funds privately to a new address without changing assets — breaking the on-chain link between source and destination while keeping the same token. ## The Only Difference Follow the [Private Swap integration guide](/developer-hub/swap-flows/private-swap) exactly, but set `from` and `to` to the **same** token id when requesting a quote: ```javascript JavaScript theme={null} const usdtTokenId = '6689b73ec90e45f3b3e51566'; // the token to send const params = new URLSearchParams({ amount: '100', from: usdtTokenId, // same token in... to: usdtTokenId, // ...and out types: 'private', }); // ...request GET /quotes, then create the order exactly as in Private Swap ``` ```bash cURL theme={null} curl -X GET "https://api-partner.houdiniswap.com/v2/quotes?amount=100&from=6689b73ec90e45f3b3e51566&to=6689b73ec90e45f3b3e51566&types=private" \ -H "Authorization: your_api_key:your_api_secret" ``` `amountOut` will be slightly lower than `amountIn` — network and exchange fees apply per hop even when the token is the same. Use the quoted `amountOut` when setting recipient expectations. ## Next Steps The full flow this page builds on — token discovery, order creation, and status progression Understand all order statuses # Standard Swap Flow Source: https://docs.houdiniswap.com/developer-hub/swap-flows/standard-swap Integrate standard CEX swaps using the unified v2 quotes and exchanges API ## Overview Standard swaps route through a single centralized exchange (CEX) for fast execution. In API v2, all swap types — standard, private, and DEX — share the same `/quotes` and `/exchanges` endpoints. Pass `types=standard` to get only standard quotes, or filter the response by `type: "standard"`. Tokens are identified by their **ID** (not symbol), and orders are created by passing a `quoteId`. **Best For**: Users who prioritize speed and want the fastest completion times (typically 3-30 minutes) with straightforward single-hop routing. Looking for the v1 standard swap guide? See [API v1 — Standard Swap](/api-v1/swap-flows/standard-swap). Need to guarantee the output amount? Add `fixed=true` and a `refundAddress` to your quote request to lock the rate. See the [Fixed Rate Swap Guide](/developer-hub/swap-flows/fixed-rate-swap) for the full flow. ## How It Works Bulk fetch CEX-supported tokens and cache to your DB, or search by name/symbol. Note each token's `id`. Call `GET /quotes` with token IDs. The response returns quotes from all available providers — select the one with `type: "standard"`. Call `POST /exchanges` with the selected `quoteId` and the user's destination address. Send exactly `inAmount` of the input token to the `depositAddress` returned in the order. Poll `GET /orders/{houdiniId}` until `statusLabel` is `FINISHED`. ## Integration Guide ### Step 1: Get Tokens There are two approaches for getting tokens. Choose the one that fits your integration: Fetch all CEX-supported tokens once and store them in your backend database. This is the best approach for production integrations — it keeps your UI fast and avoids hammering the API. Cache the token list in your backend database. Load on server startup or via a scheduled job, and refresh periodically (e.g., every 24 hours). Never call `/tokens` on every user request. ```javascript JavaScript theme={null} // Paginate through all CEX tokens and store in your DB async function fetchAllCexTokens() { let page = 1; let allTokens = []; while (true) { const response = await fetch( `https://api-partner.houdiniswap.com/v2/tokens?hasCex=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?hasCex=true&pageSize=20&page=1" \ -H "Authorization: your_api_key:your_api_secret" ``` Search for specific tokens by name or symbol on demand. Useful for lightweight integrations or when you want to let users search without prefetching everything. ```javascript JavaScript theme={null} // Search tokens as user types async function searchTokens(query) { const params = new URLSearchParams({ term: query, // e.g. "bitcoin" or "ETH" hasCex: '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=bitcoin&hasCex=true&pageSize=20&page=1" \ -H "Authorization: your_api_key:your_api_secret" ``` ### Step 2: Get Quotes Call `GET /quotes` using token IDs (not symbols). The response includes quotes from all available providers across all swap types. ```javascript JavaScript theme={null} const params = new URLSearchParams({ amount: '1', from: '6689b73ec90e45f3b3e51566', // ETH token id from /tokens to: '6689b73ec90e45f3b3e51558', // USDC token id from /tokens types: 'standard', // only return standard quotes }); 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(); // Pick a standard (single-hop CEX) quote const standardQuote = quotes.find(q => q.type === 'standard'); console.log('Provider:', standardQuote.swapName); console.log('Amount out:', standardQuote.amountOut); console.log('ETA:', standardQuote.duration, 'minutes'); console.log('Quote ID:', standardQuote.quoteId); // needed for next step ``` ```bash cURL theme={null} curl -X GET "https://api-partner.houdiniswap.com/v2/quotes?amount=1&from=6689b73ec90e45f3b3e51566&to=6689b73ec90e45f3b3e51558&types=standard" \ -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" ``` ### Quotes Response ```json theme={null} { "quotes": [ { "quoteId": "69af9e02f9c5affabcaccc14", "type": "standard", "swap": "cc", "swapName": "Coincraddle", "logoUrl": "https://api.houdiniswap.com/assets/logos/coincraddle.jpg", "amountIn": 1, "amountOut": 23.56, "amountOutUsd": 2019.09, "min": 0.098169, "max": 4688.581529, "duration": 30, "rewardsAvailable": true } ], "total": 5 } ``` **Key Fields:** * `quoteId`: Pass this to `/exchanges` to create the order * `type`: `"standard"` for single-hop CEX routing * `swap` / `swapName`: CEX provider code and human-readable name * `amountOut`: Output amount * `duration`: Estimated completion time in minutes * `min` / `max`: Valid input amount range ### Step 3: Create Order Pass the `quoteId` and destination address to `POST /exchanges`: ```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: '694cd4ef6ca7023b5e00a288', // from /quotes addressTo: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' }) }); const order = await response.json(); console.log('Order ID:', order.houdiniId); console.log('Deposit to:', order.depositAddress); console.log('Send amount:', order.inAmount, order.inSymbol); console.log('Expires:', order.expires); ``` ```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": "694cd4ef6ca7023b5e00a288", "addressTo": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e" }' ``` ### Order Response ```json theme={null} { "houdiniId": "iBQMRX3xvXrFMGQi71ogo9", "created": "2025-12-25T06:13:46.673Z", "expires": "2025-12-25T06:43:46.673Z", "depositAddress": "0x7364a0b6c55004427a4a7c26355ce9c75ef56194", "receiverAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "anonymous": false, "status": -1, "statusLabel": "NEW", "inAmount": 1, "inSymbol": "ETH", "inStatus": 0, "inStatusLabel": "NEW", "outAmount": 2456.78, "outSymbol": "USDC", "outStatus": 0, "outStatusLabel": "NEW", "eta": 3, "inAmountUsd": 2937, "swapName": "Changelly", "inToken": { "id": "6689b73ec90e45f3b3e51566", "symbol": "ETH", "name": "Ethereum", "decimals": 18, "chain": "ethereum" }, "outToken": { "id": "6689b73ec90e45f3b3e51558", "symbol": "USDC", "name": "USD Coin", "decimals": 6, "chain": "ethereum" } } ``` **Key Response Fields:** * `houdiniId`: Unique order identifier — use for status polling * `depositAddress`: Send input funds here * `inAmount` / `inSymbol`: Exact amount and token to send * `expires`: Deposit deadline (typically 30 minutes) * `statusLabel`: Human-readable order status * `eta`: Estimated completion time in minutes Send exactly `inAmount` of `inSymbol` to `depositAddress` before `expires`. ### Step 4: Send Deposit After creating the order, send the input tokens to `depositAddress`. The status will advance automatically once the deposit is detected on-chain. ### Step 5: 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: ```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 deposit // CONFIRMING — deposit detected, awaiting confirmations // EXCHANGING — CEX is processing the swap // FINISHED — swap complete, funds sent // FAILED — swap failed // EXPIRED — deposit not received in time // REFUNDED — funds returned to sender ``` ```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" ``` ### Status Progression ``` NEW / WAITING ↓ Deposit sent and detected CONFIRMING ↓ Blockchain confirmations received EXCHANGING ↓ CEX processes the swap FINISHED ``` Poll every 30 seconds. Standard swaps typically complete in 3–30 minutes. For real-time updates without polling, use the [WebSocket API](/developer-hub/core-concepts/websocket-order-updates). ## Best Practices * **Bulk fetch + cache**: Paginate through `/v2/tokens?hasCex=true` on server startup and store in your DB. Refresh every 24 hours. * **Search**: Use `?term=` for on-demand token lookup — good for lightweight integrations. * Never call `/tokens` on every user request in production. * Poll `/orders/{houdiniId}` every 30 seconds * Store `houdiniId` for future reference and support lookups * Handle all `statusLabel` values: FINISHED, FAILED, EXPIRED, REFUNDED * Re-fetch quotes if quote is expired before calling `/exchanges` * Validate `addressTo` format before submitting * Implement retry logic with backoff for API calls * Never expose API keys in frontend code * Use backend-only API integration * Validate all addresses before submitting * Store order records for audit trail ## Common Issues **Causes**: Network congestion, CEX processing delays, slow block confirmations. **Solution**: Continue monitoring. Most swaps complete within 2× the estimated time. **Issue**: User sent an incorrect amount to the deposit address. **Solution**: A partial refund may be processed. Contact support with the `houdiniId`. **Cause**: Deposit was not received before the `expires` timestamp. **Solution**: Fetch a new quote and create a new order. **Solution**: Wait for blockchain confirmations. Check the deposit transaction on a block explorer and verify the correct amount was sent. ## Example Repositories See full working integrations on GitHub: Full Next.js integration showing standard, private, and DEX swap flows Backend Node.js integration with token fetching, quoting, and order tracking ## Next Steps Multi-hop privacy swaps On-chain decentralized swaps Understand all order statuses Handle errors and edge cases # Error Codes & Troubleshooting Source: https://docs.houdiniswap.com/developer-hub/troubleshooting/error-codes-and-troubleshooting Complete reference of error codes, HTTP statuses, and response formats for the HoudiniSwap Partner API v2. ## Response Format All v2 error responses follow this shape: ```json theme={null} { "code": "QUOTE_OVER_LIMIT", "message": "Unable to perform exchange, quote over 100000 USD", "requestId": "94eb4a6f-12ca-4f22-9d0c-7ffd65ace1c7" } ``` | Field | Type | Description | | ----------- | ------ | ----------------------------------------------------------------------------- | | `code` | string | Machine-readable error code (stable enum value). **Match on this field.** | | `message` | string | Human-readable description. May contain dynamic values — use regex if needed. | | `requestId` | string | Unique request identifier for support/debugging. | Always match on the `code` string, not the HTTP status or message text. The `code` values are a stable enum and won't change without a breaking version bump. ### Special Response Shapes **Validation errors** include a `fields` object with per-field details: ```json theme={null} { "code": "VALIDATION_ERROR", "message": "Validation Failed", "requestId": "...", "fields": { "amount": { "message": "Amount must be positive", "value": -5 } } } ``` **Rate limit errors** include retry metadata: ```json theme={null} { "code": "RATE_LIMIT_EXCEEDED", "message": "FREE tier: 20 quote requests per minute. Try again in 45 seconds.", "requestId": "...", "retryAfter": 45, "meta": { "retryAfter": 45, "limit": 20, "windowType": "minute", "operationType": "quote", "tier": "free" } } ``` *** ## Error Codes by Endpoint ### `GET /v2/quotes` | HTTP | Code | Message | Notes | | ---- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | | 422 | `VALIDATION_ERROR` | Validation Failed | Missing/invalid params. Includes `fields` object. | | 422 | `QUOTE_OVER_LIMIT` | Dynamic max value. | | | 422 | `AMOUNT_TOO_LOW` | Dynamic min value. | | | 422 | `UNSUPPORTED_FROM_TOKEN` | Unsupported \`from\` Token | Token ID not found or disabled. | | 422 | `UNSUPPORTED_TO_TOKEN` | Unsupported \`to\` Token | Token ID not found or disabled. | | 422 | `TO_AND_FROM_CANNOT_BE_THE_SAME` | \`to\` and \`from\` cannot be both the same token | | | 422 | `UNSUPPORTED_ANON_TOKEN` | Unsupported \`anonymousToken\` | Invalid intermediary token for private quotes. | | 422 | `SWAP_AMOUNT_IS_OUT_OF_BOUNDS` | Provider-level min/max exceeded. | | | 422 | `XMR_SWAP_AMOUNT_IS_OUT_OF_BOUNDS` | Private swap intermediary bounds exceeded. | | | 429 | `RATE_LIMIT_EXCEEDED` | Includes `retryAfter` and `meta`. | | | 503 | `PRICE_QUOTES_NOT_RETRIEVED` | Could not retrieve price quotes. Try to use a different amount or a different pair. If using Private mode, try Semi-Private | All providers failed to return quotes. | | 503 | `ANONYMOUS_DISABLED` | Anonymous exchanges are temporarily disabled. Please contact support for more details! | Feature flag is off. | | 500 | `INTERNAL_SERVER_ERROR` | Internal Server Error | Catch-all for unexpected errors. | ### `POST /v2/exchanges` | HTTP | Code | Message | Notes | | ---- | ----------------------------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | 422 | `VALIDATION_ERROR` | Validation Failed | Missing/invalid body fields. Also: expired quote (message includes max age). | | 422 | `INVALID_QUOTE` | Invalid Quote | Quote not found, expired, or wrong type. | | 422 | `INVALID_PATH` | Path is invalid | The swap route/path from the quote is invalid or unavailable. | | 422 | `ADDRESS_TO_INVALID` | addressTo is invalid | Generic address validation failure. | | 422 | `ADDRESS_TO_INVALID_FOR_CHAIN` | Chain-specific address validation failure. | | | 422 | `ADDRESS_FROM_INVALID` | addressFrom is invalid | Missing or invalid source address (required for DEX). | | 422 | `ADDRESS_FROM_INVALID_FOR_CHAIN` | Chain-specific source address validation failure. `{type}` is "from" or "to". | | | 422 | `ADDRESS_TO_IN_DEPOSIT_LOG` | addressTo cannot be used as it exists in deposit log | Address reuse prevention. | | 422 | `ADDRESS_TO_IS_TOKEN_ADDRESS` | addressTo cannot be a token contract address | Sending to a contract address is blocked. | | 422 | `X_ADDRESS_NOT_SUPPORTED` | X-Address not supported yet! | XRP X-format addresses not supported. | | 422 | `REUSED_DEPOSIT_ADDRESS` | Deposit address collision detected. | | | 422 | `SWAP_AMOUNT_IS_OUT_OF_BOUNDS` | Provider-level bounds exceeded at exchange time. | | | 422 | `XMR_SWAP_AMOUNT_IS_OUT_OF_BOUNDS` | Private swap intermediary bounds exceeded. | | | 422 | `TOKEN_DISABLED` | Token was disabled between quote and exchange. | | | 422 | `INVALID_SWAP` | Unknown or disabled swap provider. | | | 422 | `ORDER_ALREADY_INITIALIZING_OR_PROCESSED` | Order already INITIALIZING or processed | Duplicate exchange attempt. | | 422 | `REFUND_ADDRESS_REQUIRED_FOR_FIXED_RATE` | Refund address is required for fixed rate exchanges | `refundAddress` was omitted when using a `fixed=true` quote. Always include it for fixed rate orders. | | 422 | `FIXED_RATE_QUOTE_EXPIRED` | Fixed rate quote has expired | `validUntil` has passed, or the provider lost fixed-rate capability after the quote was issued. Re-fetch a new quote. | | 422 | `ANONYMOUS_AND_FIXED_RATE_NOT_SUPPORTED` | Anonymous and fixed rate are not supported together | Cannot combine `fixed=true` with private/anonymous routing. Use a standard quote instead. | | 422 | `DEX_AND_FIXED_RATE_NOT_SUPPORTED` | DEX and fixed rate are not supported together | Cannot use `fixed=true` with DEX quotes. Fixed rate is only available for standard (CEX) swaps. | | 429 | `RATE_LIMIT_EXCEEDED` | (same pattern as quotes) | Includes `retryAfter` and `meta`. | | 500 | `UNABLE_TO_PERFORM_EXCHANGE` | Unable to perform exchange, no available paths | All providers failed during execution. | | 503 | `ANONYMOUS_DISABLED` | Anonymous exchanges are temporarily disabled... | Feature flag is off. | | 404 | `NOT_FOUND` | Not found | Post-creation order lookup failed (edge case). | | 500 | `INTERNAL_SERVER_ERROR` | Internal Server Error | Catch-all. | ### `GET /v2/orders/{houdiniId}` and `GET /v2/orders` | HTTP | Code | Message | Notes | | ---- | ----------------------- | --------------------- | ------------------------------------------------------ | | 404 | `NOT_FOUND` | Not found | Order doesn't exist or belongs to a different partner. | | 422 | `VALIDATION_ERROR` | Validation Failed | Invalid query parameters. | | 429 | `RATE_LIMIT_EXCEEDED` | (same pattern) | | | 500 | `INTERNAL_SERVER_ERROR` | Internal Server Error | Catch-all. | ### Global Errors (all endpoints) These can be returned by any endpoint: | HTTP | Code | Message | Notes | | ---- | -------------------------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------- | | 400 | `MISSING_API_CREDENTIALS` | Missing API credentials | No `Authorization` header or `partner-id` header provided. | | 400 | `INVALID_API_CREDENTIALS_FORMAT` | Invalid API credentials format | `Authorization` header is malformed (expected `id:secret`). | | 401 | `INVALID_API_CREDENTIALS` | Invalid API credentials | Credentials don't match any active partner. | | 401 | `AUTHENTICATION_FAILED` | Authentication failed | Auth check threw an unexpected error. | | 401 | `INVALID_SECURITY_SCHEME` | Invalid security scheme | Request used an unsupported authentication method. Internal safeguard — should not occur in normal usage. | | 403 | `ACCESS_DENIED` | Access restricted for partner-id requests | Operation not allowed for public `partner-id` access. | | 404 | `ROUTE_NOT_FOUND` | Route not found | The requested URL path doesn't match any v2 endpoint. | | 429 | `RATE_LIMIT_EXCEEDED` | (dynamic — see special shape above) | Per-partner, per-operation rate limits. | | 503 | `SERVICE_UNAVAILABLE` | Service Unavailable | Backend service is temporarily unavailable. | | 500 | `INTERNAL_SERVER_ERROR` | Internal Server Error | Unhandled exception. | *** ## HTTP Status Summary | Status | Meaning | Code Strings | | ------ | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 400 | Bad Request | `MISSING_API_CREDENTIALS`, `INVALID_API_CREDENTIALS_FORMAT` | | 401 | Unauthorized | `INVALID_API_CREDENTIALS`, `AUTHENTICATION_FAILED`, `INVALID_SECURITY_SCHEME` | | 403 | Forbidden | `ACCESS_DENIED` | | 404 | Not Found | `NOT_FOUND`, `ROUTE_NOT_FOUND` | | 422 | Unprocessable Entity | `VALIDATION_ERROR`, `QUOTE_OVER_LIMIT`, `AMOUNT_TOO_LOW`, `UNSUPPORTED_FROM_TOKEN`, `UNSUPPORTED_TO_TOKEN`, `TO_AND_FROM_CANNOT_BE_THE_SAME`, `UNSUPPORTED_ANON_TOKEN`, `SWAP_AMOUNT_IS_OUT_OF_BOUNDS`, `XMR_SWAP_AMOUNT_IS_OUT_OF_BOUNDS`, `ADDRESS_TO_INVALID`, `ADDRESS_TO_INVALID_FOR_CHAIN`, `ADDRESS_FROM_INVALID`, `ADDRESS_FROM_INVALID_FOR_CHAIN`, `ADDRESS_TO_IN_DEPOSIT_LOG`, `ADDRESS_TO_IS_TOKEN_ADDRESS`, `X_ADDRESS_NOT_SUPPORTED`, `REUSED_DEPOSIT_ADDRESS`, `TOKEN_DISABLED`, `INVALID_SWAP`, `ORDER_ALREADY_INITIALIZING_OR_PROCESSED`, `INVALID_QUOTE`, `INVALID_PATH`, `REFUND_ADDRESS_REQUIRED_FOR_FIXED_RATE`, `FIXED_RATE_QUOTE_EXPIRED`, `ANONYMOUS_AND_FIXED_RATE_NOT_SUPPORTED`, `DEX_AND_FIXED_RATE_NOT_SUPPORTED` | | 429 | Too Many Requests | `RATE_LIMIT_EXCEEDED` | | 500 | Internal Server Error | `INTERNAL_SERVER_ERROR`, `UNABLE_TO_PERFORM_EXCHANGE` | | 503 | Service Unavailable | `SERVICE_UNAVAILABLE`, `PRICE_QUOTES_NOT_RETRIEVED`, `ANONYMOUS_DISABLED` | *** ## Best Practices 1. **Match on `code`, not `message`** — The `code` field is a stable string enum. Messages may contain dynamic values and can change without notice. 2. **Use regex for dynamic messages** — For codes like `QUOTE_OVER_LIMIT`, `AMOUNT_TOO_LOW`, `SWAP_AMOUNT_IS_OUT_OF_BOUNDS`, extract the dynamic part with a regex: ```text theme={null} /Unable to perform exchange, quote over (\d+) USD/ /Amount is too low, minimum is ([\d.]+) USD/ ``` 3. **Handle 422 as your primary error status** — Most business logic errors return 422, not 400 or 500. 4. **Respect `retryAfter`** — On 429 responses, wait the specified number of seconds before retrying. The `meta` object provides additional context about which limit was hit. 5. **Log `requestId`** — Always log the `requestId` from error responses. Include it when contacting support for faster debugging. 6. **Implement exponential backoff for 500/503** — These are transient errors. Retry with backoff (e.g. 1s, 2s, 4s) up to 3 attempts. 7. **Don't retry 422 errors** — These are deterministic. The same request will produce the same error. Fix the input before retrying. ## Need Help? Complete endpoint documentation Understand order states Integration guides for each swap type Get help with specific issues # Error Codes & Troubleshooting API v1 Source: https://docs.houdiniswap.com/developer-hub/troubleshooting/errors Common errors, their causes, and how to resolve them Deprecation of the REST v1 swap endpoints begins on September 30, 2026. Please migrate to the Partner API v2. Dashboard and authentication endpoints remain on v1 GraphQL and are not affected. [Read the migration guide](https://docs.houdiniswap.com/migration/v1-to-v2) ## Error Response Format All API errors return a JSON object with the following structure: ```json theme={null} { "name": "Error", "message": "Error message", "code": 500 } ``` | Field | Description | | --------- | --------------------------------- | | `name` | Always `"Error"` | | `message` | Human-readable error description | | `code` | HTTP status code (400, 500, etc.) | Use the `message` field to understand the specific error. The `code` field reflects the HTTP status code. *** ## Common Errors ### Token & Address Errors | Message | Solution | | --------------------------------------------- | ------------------------------------------------------------------------- | | Unsupported `from` Token | Check `/tokens?hasCex=true` or `/tokens?hasDex=true` for supported tokens | | Unsupported `to` Token | Check `/tokens?hasCex=true` or `/tokens?hasDex=true` for supported tokens | | addressTo is invalid | Validate using `addressValidation` regex from `/chains` | | addressFrom is invalid | Validate using `addressValidation` regex from `/chains` | | `to` and `from` cannot be both the same token | From and To tokens only able to be the same token if private | | Token not found | Token ID/symbol doesn't exist | | Network not found | Check `/chains` for supported networks | | Address is not valid for this chain | Validate using `addressValidation` regex from `/chains` | ### Quote & Amount Errors | Message | Solution | | -------------------------------------------- | ------------------------------------------------------------ | | Invalid Quote | Quote expired or invalid, request a new one | | Path is invalid | Route not available, try different pair | | `amount` is out of bounds for swap | Check `min` and `max` from quote response | | Unable to perform exchange, quote over X USD | Reduce swap amount | | Amount is too low, minimum is X USD | Increase swap amount | | Could not retrieve price quotes | Try different amount or pair. For Private mode, try Standard | ### Order Errors | Message | Solution | | --------------- | ----------------------------- | | Order not found | Verify `houdiniId` is correct | | Swap not found | Swap ID doesn't exist | ### Exchange & Routing Errors | Message | Solution | | -------------------------------------------------------------------------------- | ------------------------------------------ | | Unable to perform exchange, no available paths | Try different pair or amount | | Unable to perform exchange, no available paths. Tip: try toggling OFF Exact mode | Disable fixed/exact mode | | Unable to generate swap\_hop\_1 (anon) | Private routing failed - try Standard swap | | Unable to generate swap\_hop\_2 (anon) | Private routing failed - try Standard swap | ### Authentication & Account Errors | Message | Solution | | -------------------------- | -------------------------- | | Please define your API key | Add `Authorization` header | | Incorrect credentials | Verify API key and secret | ### Service Errors | Message | Solution | | --------------------------------------------- | ------------------------------------ | | Something went wrong. Please contact support! | Retry or contact support | | Your request has timed-out | Retry the request | | Services temporary unavailable | Wait and retry | | Anonymous exchanges are temporarily disabled | Use Standard swap or contact support | ### DEX-Specific Errors | Message | Solution | | ------------------------------------- | ------------------------------------------------ | | This chain is currently not supported | Check `/tokens?hasDex=true` for supported chains | | Swap not supported | Pair not available for DEX swap | *** ## HTTP Status Codes | Status | Meaning | Common Causes | | ------- | --------------------- | ---------------------------------- | | **400** | Bad Request | Invalid parameters, malformed JSON | | **401** | Unauthorized | Missing or invalid API credentials | | **403** | Forbidden | API key lacks permissions | | **404** | Not Found | Resource doesn't exist | | **429** | Too Many Requests | Rate limit exceeded | | **500** | Internal Server Error | Server-side issue | | **503** | Service Unavailable | Maintenance or partner downtime | *** ## Need Help? Complete endpoint documentation Understand order states Integration guides for each swap type Get help with specific issues # Widget Overview Source: https://docs.houdiniswap.com/developer-hub/widget/overview Drop-in swap widget you embed into any website with a single iframe ## What is the Houdini Widget? The Houdini Widget is a pre-built, hosted swap interface that you embed into any website or web application with a single `