# Table of Contents - [Home | Developer Docs | Titan](#home-developer-docs-titan) - [Welcome to Titan | Titan](#welcome-to-titan-titan) - [Quickstart | Titan](#quickstart-titan) - [Prime Mode and Custom Settings | Titan](#prime-mode-and-custom-settings-titan) - [Titan DEX Integrations | Titan](#titan-dex-integrations-titan) - [Private Beta | Titan](#private-beta-titan) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Analytics | Titan](#analytics-titan) - [Unknown](#unknown) - [Titan Private Swaps | Titan](#titan-private-swaps-titan) - [Unknown](#unknown) - [Unknown](#unknown) - [Titan Limit Orders | Titan](#titan-limit-orders-titan) - [Usernames and Referrals | Titan](#usernames-and-referrals-titan) - [How Swaps Work | Titan](#how-swaps-work-titan) - [Unknown](#unknown) - [Titan's Unique Algorithm | Titan](#titan-s-unique-algorithm-titan) - [DEX Aggregators | Titan](#dex-aggregators-titan) - [Meta Aggregation | Titan](#meta-aggregation-titan) - [Unknown](#unknown) - [Titan DART | Titan](#titan-dart-titan) - [Unknown](#unknown) - [DART Routing | Titan](#dart-routing-titan) - [Authentication | Developer Docs | Titan](#authentication-developer-docs-titan) - [Introduction | Developer Docs | Titan](#introduction-developer-docs-titan) - [Get API Access | Developer Docs | Titan](#get-api-access-developer-docs-titan) - [Guides | Developer Docs | Titan](#guides-developer-docs-titan) - [Quickstart | Developer Docs | Titan](#quickstart-developer-docs-titan) - [Get API Access | Developer Docs | Titan](#get-api-access-developer-docs-titan) - [Limit Order Events | Developer Docs | Titan](#limit-order-events-developer-docs-titan) - [Swap V2 vs Swap V3 | Developer Docs | Titan](#swap-v2-vs-swap-v3-developer-docs-titan) - [Quote Price | Developer Docs | Titan](#quote-price-developer-docs-titan) - [StopStream | Developer Docs | Titan](#stopstream-developer-docs-titan) - [API Reference | Developer Docs | Titan](#api-reference-developer-docs-titan) - [API Reference | Developer Docs | Titan](#api-reference-developer-docs-titan) - [AI / LLM Integration | Developer Docs | Titan](#ai-llm-integration-developer-docs-titan) - [Community & Support | Developer Docs | Titan](#community-support-developer-docs-titan) - [Connection & Negotiation | Developer Docs | Titan](#connection-negotiation-developer-docs-titan) - [GetInfo | Developer Docs | Titan](#getinfo-developer-docs-titan) - [How to Use | Developer Docs | Titan](#how-to-use-developer-docs-titan) - [Titan Direct | Developer Docs | Titan](#titan-direct-developer-docs-titan) - [Guides | Developer Docs | Titan](#guides-developer-docs-titan) - [Titan Gateway | Developer Docs | Titan](#titan-gateway-developer-docs-titan) - [Error Codes | Developer Docs | Titan](#error-codes-developer-docs-titan) - [GetVenues / ListProviders | Developer Docs | Titan](#getvenues-listproviders-developer-docs-titan) - [Lifecycle & Polling | Developer Docs | Titan](#lifecycle-polling-developer-docs-titan) - [Overview | Developer Docs | Titan](#overview-developer-docs-titan) - [Titan Direct vs Titan Gateway | Developer Docs | Titan](#titan-direct-vs-titan-gateway-developer-docs-titan) - [Info / Venues / Providers | Developer Docs | Titan](#info-venues-providers-developer-docs-titan) - [GetSwapPrice | Developer Docs | Titan](#getswapprice-developer-docs-titan) - [Platform Fees | Developer Docs | Titan](#platform-fees-developer-docs-titan) - [Overview | Developer Docs | Titan](#overview-developer-docs-titan) - [Placing Taker Orders | Developer Docs | Titan](#placing-taker-orders-developer-docs-titan) - [Types Reference | Developer Docs | Titan](#types-reference-developer-docs-titan) - [Withdrawals | Developer Docs | Titan](#withdrawals-developer-docs-titan) - [Limits & Idempotency | Developer Docs | Titan](#limits-idempotency-developer-docs-titan) - [Error Handling & Reconnect | Developer Docs | Titan](#error-handling-reconnect-developer-docs-titan) - [Authentication | Developer Docs | Titan](#authentication-developer-docs-titan) - [Transaction Template | Developer Docs | Titan](#transaction-template-developer-docs-titan) - [Configure Routing | Developer Docs | Titan](#configure-routing-developer-docs-titan) - [Onboarding (SIWS) | Developer Docs | Titan](#onboarding-siws-developer-docs-titan) - [Unknown](#unknown) - [Quote Swap | Developer Docs | Titan](#quote-swap-developer-docs-titan) - [Error Codes | Developer Docs | Titan](#error-codes-developer-docs-titan) - [SDK Reference | Developer Docs | Titan](#sdk-reference-developer-docs-titan) - [Quickstart | Developer Docs | Titan](#quickstart-developer-docs-titan) - [Fee Collection | Developer Docs | Titan](#fee-collection-developer-docs-titan) - [Overview | Developer Docs | Titan](#overview-developer-docs-titan) - [Wire Protocol | Developer Docs | Titan](#wire-protocol-developer-docs-titan) - [Order & Execution Schema | Developer Docs | Titan](#order-execution-schema-developer-docs-titan) - [Endpoints | Developer Docs | Titan](#endpoints-developer-docs-titan) - [Create & Manage Orders | Developer Docs | Titan](#create-manage-orders-developer-docs-titan) - [Error Codes | Developer Docs | Titan](#error-codes-developer-docs-titan) - [NewSwapQuoteStream | Developer Docs | Titan](#newswapquotestream-developer-docs-titan) - [Stream & Execute a Swap | Developer Docs | Titan](#stream-execute-a-swap-developer-docs-titan) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Overview | Developer Docs | Titan](#overview-developer-docs-titan) --- # Home | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/home.md) . ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F3919091904-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FOcDzWOE77OJI2Qje9dcH%252Fuploads%252Fgit-blob-7ad244c7e4f10bcb574a05cddb166c010750826d%252Fimage.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=c03019a9&sv=2) Titan is the swap meta-aggregator behind the best execution on Solana. Quotes from multiple providers, simulated on-chain by **Argos**, returned as ready-to-sign transactions. **New: DART Swap API** — Try Titan's on-chain dynamic routing for free. No API key, JSON responses, instant quotes. [Get started →](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/overview) * * * Why Titan[](https://titan-exchange.gitbook.io/titan/developer-doc#why-titan) ----------------------------------------------------------------------------- The same infrastructure that powers titan.ag is available to you as production-grade APIs. **Best Execution** 70–75% win rate vs all other routing sources. **No Platform Fees** Zero subscription or trading fees. Save ~20 bps. **Low Slippage** Active simulations and algorithmic route selection. * * * APIs[](https://titan-exchange.gitbook.io/titan/developer-doc#apis) ------------------------------------------------------------------- Two interfaces, same Argos routing engine. Pick the one that fits your architecture. [](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection) **Titan Direct** WebSocket streaming quotes in real time. [](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap) **Titan Gateway** REST endpoints, same Argos routing. [](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway) **Direct vs Gateway** Choose the right interface for your use case. * * * Get Started[](https://titan-exchange.gitbook.io/titan/developer-doc#get-started) --------------------------------------------------------------------------------- Everything you need to make your first request. [](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/quickstart) **Quickstart** First swap quote in under 5 minutes. [](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/authentication) **Authentication** Pass your token via header or query param. [](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/api-access) **Get API Access** Get a token from Titan, Triton, or QuickNode. * * * Guides[](https://titan-exchange.gitbook.io/titan/developer-doc#guides) ----------------------------------------------------------------------- Step-by-step walkthroughs for common integration tasks. [](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute) **Stream & Execute** Stream quotes, build tx, send on-chain. [](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing) **Configure Routing** Filter venues, providers, and route types. [](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection) **Fee Collection** Collect platform fees with feeAccount. * * * Resources[](https://titan-exchange.gitbook.io/titan/developer-doc#resources) ----------------------------------------------------------------------------- SDKs, AI tools, and community channels. [](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk) **SDK Reference** TypeScript and Rust SDKs. [](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration) **AI / LLM Integration** Claude Code skill and llms.txt. [](https://titan-exchange.gitbook.io/titan/developer-doc/resources/community-and-support) **Community & Support** Discord, GitHub, and support. Last updated 3 months ago --- # Welcome to Titan | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/readme.md) . ![Page cover](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FWxdi8oOhGoM2YwKx3SRu%252Fgitbook%2520background.png%3Falt%3Dmedia%26token%3D48a343f2-5bca-4adc-968b-1a9fc85deb81&width=1248&dpr=3&quality=100&sign=b4526886&sv=2) Outperformance on your Token Swaps[](https://titan-exchange.gitbook.io/titan#outperformance-on-your-token-swaps) ----------------------------------------------------------------------------------------------------------------- Start swapping your tokens now at [https://app.titan.exchange/](https://app.titan.exchange/) . Titan is driven by one overarching goal to help all users within the Solana ecosystem: ### _Experience Outperformance._[](https://titan-exchange.gitbook.io/titan#experience-outperformance) With Solana powering low latency low cost transactions, people can finally take back control, find the best prices, and minimize unnecessary middle man costs. The challenge is that with so much data living on-chain, how can users be sure that they are getting the best deal? #### Titan's Products[](https://titan-exchange.gitbook.io/titan#titans-products) To achieve this goal, Titan currently has 3 main features: * Unique Spot Swap Routes (DEX Aggregator) * Titan has developed its own unique algorithm called Argos that fixes problems associated with current solutions in finding the best trade routes. This has resulted in better prices for users 80% of the time. * Meta Aggregation on Solana * To ensure that users always get the best deal, Titan aggregates multiple aggregators and will route the user to the best quote with no fees attached. * Titan Prime Mode * Titan Prime automatically optimizes your swap settings — including slippage and transaction landing — to deliver the best execution through Titan’s meta-aggregator. Everything on Titan is dedicated to getting you the outperformance you deserve. The statistics on how much you have gained by using Titan's platform will be shown on the UI page, along with volume and referrals. Titan's API for integrations and systematic traders will be released soon. Please contact us for more details. [NextQuickstart](https://titan-exchange.gitbook.io/titan/getting-started/quickstart) Last updated 7 months ago --- # Quickstart | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/getting-started/quickstart.md) . Titan does not charge any fees on basic swaps. The underlying DEXes or DEX Aggregators that have been incorporated may charge fees To start interacting with the platform, please connect your wallet. There is a large selection of wallets that you may choose from. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252Fb1me2FmTzPV8tEWvj75h%252FScreen%2520Shot%25202025-09-21%2520at%252011.03.22%2520AM.png%3Falt%3Dmedia%26token%3Df4a17ce3-1bf4-4b26-91bb-3ac777222d34&width=768&dpr=3&quality=100&sign=5779744f&sv=2) Once connected, you are able to interact with the swap interface. You can also see the cumulative total of your trading volume with Titan, as well as your total outperformance and edge gained as a result of using Titan over other platforms. Please note that ledger wallet integrations are supported with these wallets. ### How to do a Swap[](https://titan-exchange.gitbook.io/titan/getting-started/quickstart#how-to-do-a-swap) You must first select your input and output tokens that you want to trade. The input tokens are sorted based on the USD value of the tokens in your wallet, while the output tokens are shown based on popularity and relevance. Once the tokens are selected, you can select the input token amount you want to trade and quotes will populate to fill in the estimated amount out. Please note that specifying the amount out as of this time is not allowed. You will receive quotes from multiple aggregators. Titan will populate the estimated amount out as well as the relevant transaction details from the best quote. Users can then approve and execute the transaction. Please note that quotes refresh every second. The quotes themselves are simulated live on the blockchain to provide a live stream of the most up to date output given the routes. Titan provides the best on-chain estimate of the actual output of a given quote. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252Ff3adxeLunL4sYzold0gq%252FScreen%2520Shot%25202025-08-13%2520at%25201.36.05%2520PM.png%3Falt%3Dmedia%26token%3D61b7e4e1-1933-4c30-af22-5a3694c30bda&width=768&dpr=3&quality=100&sign=d40495b4&sv=2) The number of pools and DEXes involved in the chosen route will also be shown. Users can then click on the Swap button to execute through their connected wallet application. ### Trade Settings[](https://titan-exchange.gitbook.io/titan/getting-started/quickstart#trade-settings) In addition, trades can be sized quickly by selecting the Max or % buttons in the input bar. The % button allows users to have fine grain control over the amount of the input tokens they want to trade. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FpiigCK2Ucdc2yWLEREEU%252FScreen%2520Shot%25202025-09-21%2520at%252011.04.47%2520AM.png%3Falt%3Dmedia%26token%3De79beba0-34e7-4296-9f15-ac261cc9e84c&width=768&dpr=3&quality=100&sign=845f3d9f&sv=2) Users can further customize their settings as described in the Custom Settings page. [PreviousWelcome to Titan](https://titan-exchange.gitbook.io/titan) [NextPrime Mode and Custom Settings](https://titan-exchange.gitbook.io/titan/getting-started/quickstart-1) Last updated 10 months ago --- # Prime Mode and Custom Settings | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/getting-started/quickstart-1.md) . ### Extra User Parameters[](https://titan-exchange.gitbook.io/titan/getting-started/quickstart-1#extra-user-parameters) ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252F0hd7Ob5OLmSv0sYVwz7t%252FScreen%2520Shot%25202025-08-13%2520at%25201.48.38%2520PM.png%3Falt%3Dmedia%26token%3D5bc1db02-93a1-4721-8178-5b73cfba8340&width=768&dpr=3&quality=100&sign=3ed03fae&sv=2) In the settings button at the top right of the swap box, users can configure their transactions and routes in a variety of ways including: * Prime vs Manual Mode * Max Slippage * Transaction Fee Methods * AMM Exclusion ### Prime Mode[](https://titan-exchange.gitbook.io/titan/getting-started/quickstart-1#prime-mode) Titan's Prime Mode automatically optimizes your swap settings — including slippage and transaction landing — to deliver the best execution through Titan’s meta-aggregator. With automatic sandwich protection, Titan Prime charges zero fees as comparable to other services that will take up to a 10 basis point fee. Along with Titan's propietary algorithm, this means users can get an extra 20 basis points per trade when using Titan Prime. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252F0hd7Ob5OLmSv0sYVwz7t%252FScreen%2520Shot%25202025-08-13%2520at%25201.48.38%2520PM.png%3Falt%3Dmedia%26token%3D5bc1db02-93a1-4721-8178-5b73cfba8340&width=768&dpr=3&quality=100&sign=3ed03fae&sv=2) When the settings button is showing an animated Prime button, Titan Prime settings will be applied to the trade. For Titan quotes, the transaction is simulated to see what the user would get at that point in time when the swap button is pressed. If this would result in a revert due to slippage tolerances being exceeded, the attempted swap would result in an error message. ### Manual Mode[](https://titan-exchange.gitbook.io/titan/getting-started/quickstart-1#manual-mode) #### Max Slippage[](https://titan-exchange.gitbook.io/titan/getting-started/quickstart-1#max-slippage) ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FoB1ekMLegVFJAkVtkERJ%252FScreen%2520Shot%25202025-08-13%2520at%25201.50.28%2520PM.png%3Falt%3Dmedia%26token%3D183475be-9140-416b-94f2-baac73baf398&width=768&dpr=3&quality=100&sign=fe653ce7&sv=2) Slippage is the amount that your final trade differs by from your quoted price when executing a trade. This could be due to several reasons, but the most common is that someone has already traded ahead of you. This setting exists so that if the slippage exceeds your max tolerance, the trade is reverted even though the transaction fee is still taken by the blockchain. There are 2 slippage settings that the user can modify: * Base Tokens: Any token pair that is not a stablecoin to stablecoin pair or SOL/LST or LST/SOL pair. * Stable/LST: Any token pair that is a stablecoin to stablecoin pair or SOL/LST or LST/SOL pair These 2 different settings are used to reflect that some tokens are very closely related in value and therefore have far less slippage involved. #### Transaction Fee Methods[](https://titan-exchange.gitbook.io/titan/getting-started/quickstart-1#transaction-fee-methods) Titan currently supports two types of broadcast mode fees in order to process transactions with plans to add more soon. These currently are: * Priority Fees ([https://solana.com/developers/guides/advanced/how-to-use-priority-fees](https://solana.com/developers/guides/advanced/how-to-use-priority-fees) ) * MEV Protect, which is a combo of Jito ([https://docs.jito.wtf/](https://docs.jito.wtf/) ) and Nozomi ([https://use.temporal.xyz/](https://use.temporal.xyz/) ) along with anti sandwich protection ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FRB6h9bsFf12dxMxM8H67%252FScreen%2520Shot%25202025-08-13%2520at%25201.51.45%2520PM.png%3Falt%3Dmedia%26token%3Dc5c92be4-4095-438d-9aa5-194046cdbfb3&width=768&dpr=3&quality=100&sign=9d068d3&sv=2) All broadcast modes have the same options included. With Auto fee mode, Titan determines the appropriate fee level to be paid leveraging 3rd party services such as Helius, Triton, and Jito depending on how fast the user wishes to process the transaction. A max cap on the fee is also set here in case the suggested fee goes above what the user is comfortable paying. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FbofQTISqSCLUxkjo8VlU%252FScreen%2520Shot%25202025-08-13%2520at%25201.52.20%2520PM.png%3Falt%3Dmedia%26token%3D28811c28-3b18-4eb2-87f2-1141a95cddfb&width=768&dpr=3&quality=100&sign=385d316a&sv=2) With Custom fee mode, the user sets the exact fee they wish to pay. Some wallets may enforce a minimum priority fee to be paid per transaction. #### AMM Exclusion[](https://titan-exchange.gitbook.io/titan/getting-started/quickstart-1#amm-exclusion) Users also have the option to exclude various AMMs from the chosen routes for whatever reason. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FqM8pCmHESpY2PHYeLu20%252FScreen%2520Shot%25202025-08-13%2520at%25201.53.14%2520PM.png%3Falt%3Dmedia%26token%3De2f95c8c-3747-4714-87ed-ee91812f989e&width=768&dpr=3&quality=100&sign=bf06b829&sv=2) The selected AMMs will then be excluded from generated routes. AMMs have been normalized across DEX Aggregators so that the user does not need to know how each DEX Aggregator handles the naming and identification of various DEXes. [PreviousQuickstart](https://titan-exchange.gitbook.io/titan/getting-started/quickstart) [NextTitan Limit Orders](https://titan-exchange.gitbook.io/titan/getting-started/titan-limit-orders) Last updated 6 months ago --- # Titan DEX Integrations | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/getting-started/titan-dex-integrations.md) . If you are interested in integrating your AMM or liquidity source into Titan, please see our guidelines at [https://github.com/Titan-Pathfinder/integration-template](https://github.com/Titan-Pathfinder/integration-template) . We handle all types of integrations, from traditional swaps to specialized mint/redeem integrations. If you have questions or a request, please reach out directly to the team on telegram or discord. [PreviousUsernames and Referrals](https://titan-exchange.gitbook.io/titan/getting-started/usernames-and-referrals) [NextTitan DART](https://titan-exchange.gitbook.io/titan/getting-started/titan-dart) Last updated 7 months ago --- # Private Beta | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/getting-started/publish-your-docs.md) . As Titan gets ready to invite all users onboard, a select group of users will be onboarded onto a private beta. This beta is necessary to facilitate user feedback, make optimizations, and ensure that the required infrastructure is set up to support user demand. To sign up for the private beta, please visit [https://titan.exchange/](https://titan.exchange/) where an email can be submitted to the private beta. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FpOAq9izBUKWAoJCJ3elS%252FScreen%2520Shot%25202025-08-13%2520at%25201.42.47%2520PM.png%3Falt%3Dmedia%26token%3D78ede8de-7252-4ede-a408-6d1f7816b81b&width=768&dpr=3&quality=100&sign=72191c1c&sv=2) After submission, please sit back and wait for a confirmation email that you have been enrolled into the Titan Private Beta. ### Invite Codes[](https://titan-exchange.gitbook.io/titan/getting-started/publish-your-docs#invite-codes) An invite code will be provided to you through email. To access the beta, please go to [https://app.titan.exchange](https://app.titan.exchange/swap) . There you will be asked to connect your wallet. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FDFhySOIyRWWVez5ZOr7D%252FScreen%2520Shot%25202025-08-13%2520at%25201.43.44%2520PM.png%3Falt%3Dmedia%26token%3D9e4cb9cd-7468-4bc7-9c56-0f772dd8bf84&width=768&dpr=3&quality=100&sign=6f5a5aa7&sv=2) After you have connected your wallet, the platform will check if that wallet address has already been whitelisted. If not, then a popup appears notifying you to either sign up for the waitlist or proceed with an invite code. If you have an invite code, please click Proceed and go to the next screen. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FRUJVmLxPMSb2M4xdPkuy%252FScreen%2520Shot%25202025-08-13%2520at%25201.45.23%2520PM.png%3Falt%3Dmedia%26token%3D7eff43e3-9bd3-4a1c-b756-1a147b86e184&width=768&dpr=3&quality=100&sign=1e85d458&sv=2) After entering the invite code, your connected wallet will be whitelisted and you can start to use the Titan platform. Please note that the wallet you connect will be the only one allowed to access the beta. If there has been a mistake, please submit a support ticket on Titan's discord. We look forwards to hearing your feedback on Titan! Last updated 11 months ago --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/readme.md). # Welcome to Titan ## Outperformance on your Token Swaps Start swapping your tokens now at . Titan is driven by one overarching goal to help all users within the Solana ecosystem: ### \*Experience Outperformance.\* With Solana powering low latency low cost transactions, people can finally take back control, find the best prices, and minimize unnecessary middle man costs. The challenge is that with so much data living on-chain, how can users be sure that they are getting the best deal? #### Titan's Products To achieve this goal, Titan currently has 3 main features: \* Unique Spot Swap Routes (DEX Aggregator) \* Titan has developed its own unique algorithm called Argos that fixes problems associated with current solutions in finding the best trade routes. This has resulted in better prices for users 80% of the time. \* Meta Aggregation on Solana \* To ensure that users always get the best deal, Titan aggregates multiple aggregators and will route the user to the best quote with no fees attached. \* Titan Prime Mode \* Titan Prime automatically optimizes your swap settings — including slippage and transaction landing — to deliver the best execution through Titan’s meta-aggregator. Everything on Titan is dedicated to getting you the outperformance you deserve. The statistics on how much you have gained by using Titan's platform will be shown on the UI page, along with volume and referrals. {% hint style="info" %} Titan's API for integrations and systematic traders will be released soon. Please contact us for more details. {% endhint %} --- # Unknown \# Titan ## Products - \[Welcome to Titan\](https://titan-exchange.gitbook.io/titan/readme.md) - \[Quickstart\](https://titan-exchange.gitbook.io/titan/getting-started/quickstart.md): How to interact with the Titan platform - \[Prime Mode and Custom Settings\](https://titan-exchange.gitbook.io/titan/getting-started/quickstart-1.md): Transaction settings on Titan and how to set your own custom settings - \[Private Beta\](https://titan-exchange.gitbook.io/titan/getting-started/publish-your-docs.md) - \[Titan Limit Orders\](https://titan-exchange.gitbook.io/titan/getting-started/titan-limit-orders.md) - \[Titan Private Swaps\](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps.md) - \[Analytics\](https://titan-exchange.gitbook.io/titan/getting-started/analytics.md) - \[Usernames and Referrals\](https://titan-exchange.gitbook.io/titan/getting-started/usernames-and-referrals.md) - \[Titan DEX Integrations\](https://titan-exchange.gitbook.io/titan/getting-started/titan-dex-integrations.md) - \[Titan DART\](https://titan-exchange.gitbook.io/titan/getting-started/titan-dart.md): How to use DART when trading on Titan - \[How Swaps Work\](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/how-swaps-work.md) - \[DEX Aggregators\](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/dex-aggregators.md) - \[Titan's Unique Algorithm\](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/editor.md): How Titan provides outperformance - \[Meta Aggregation\](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/markdown.md) - \[DART Routing\](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/dart-routing.md): Titan's onchain routing engine — the first router that dynamically re-optimizes a trade at the exact moment of execution, not seconds before ## Developer Docs - \[Home\](https://titan-exchange.gitbook.io/titan/developer-doc/home.md): Build on the best swap execution on Solana. - \[Introduction\](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/introduction.md): What Titan is, what it offers, and where to start. - \[Authentication\](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/authentication.md): How to authenticate with the Titan API. - \[Get API Access\](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/api-access.md): How to get an API token to start building with Titan. - \[Quickstart\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/quickstart.md): Get your first swap quote with Titan. - \[Guides\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides.md): Step-by-step guides for common Titan Swap API workflows. - \[Swap V2 vs Swap V3\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3.md): What Swap V3 changes over V2 and how to opt in. - \[Stream & Execute a Swap\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute.md): Connect to Titan Direct, stream live quotes, and execute a swap end to end. - \[Configure Routing\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing.md): Control which venues, providers, and route shapes Titan uses when computing swap quotes. - \[Fee Collection\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection.md): Collect a fee on every swap by specifying a fee account and basis-point rate in your request. - \[Transaction Template\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template.md): Reserve room in the swap transaction for your own instructions and address lookup tables — so the route Titan returns still fits when you assemble the final transaction. - \[Error Handling & Reconnect\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling.md): Handle server errors, stream failures, and connection drops in Titan Direct integrations. - \[API Reference\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference.md): Complete API reference for Titan Direct (WebSocket) and Titan Gateway (REST). - \[Titan Direct vs Titan Gateway\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway.md): Side-by-side comparison of Titan's two integration paths. - \[Titan Direct\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct.md): Titan Direct — persistent WebSocket API with real-time streaming swap quotes. - \[Connection & Negotiation\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection.md): Connect to Titan Direct, negotiate the protocol version and compression, and send your first request. - \[GetInfo\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info.md): Read the server's protocol version and default settings before opening streams. - \[NewSwapQuoteStream\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream.md): Open a streaming swap quote that updates continuously as on-chain state changes. - \[StopStream\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream.md): Stop an active quote stream by its ID. - \[GetVenues / ListProviders\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers.md): Discover available venues and quote providers at runtime. - \[GetSwapPrice\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-swap-price.md): Get a price-only quote without transaction data. - \[Titan Gateway\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway.md): Titan Gateway — simple REST endpoints backed by the Argos routing engine. - \[Quote Swap\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap.md): Request swap quotes with executable instructions via REST. - \[Quote Price\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price.md): Get a price-only quote via REST without transaction data. - \[Info / Venues / Providers\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info.md): Server info, venues, and providers via REST. - \[Wire Protocol\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol.md): MessagePack encoding conventions, data types, and serialization rules for Titan's API. - \[Types Reference\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types.md): Quick reference index for all Titan API types — links to the page where each type is defined. - \[Error Codes\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes.md): Error codes returned by the Titan API. - \[Overview\](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/overview.md): DART — Dynamically Allocated Real Time Routing for best swap execution on Solana. - \[Get API Access\](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/get-api-access.md): How to get access to the DART Swap API. - \[How to Use\](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/how-to-use.md): Guide for the DART public endpoint — free, no API key, 1 req/sec, JSON responses. - \[Overview\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/overview.md): Server-to-server Dollar-Cost-Averaging on Solana, built on Titan-managed wallets. - \[Quickstart\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart.md): Onboard a user and create your first DCA order in four steps. - \[Guides\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides.md): Task-oriented guides for integrating Titan DCA. - \[Onboarding (SIWS)\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding.md): Bind a user's external wallet to a Titan-managed manager with one SIWS signature. - \[Create & Manage Orders\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders.md): Create, modify, pause, resume, retry, and cancel DCA orders. - \[Withdrawals\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/withdrawals.md): Return funds from a user's manager to their external wallet — wallet-level and per-order. - \[Platform Fees\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/platform-fees.md): Collect a per-cycle platform fee on DCA swaps, configured per tenant. - \[Lifecycle & Polling\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle.md): Order statuses, the withdrawal lifecycle, and how to keep your UI in sync by polling. - \[API Reference\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference.md): The complete DCA Partner API contract — auth, endpoints, schema, errors, and limits. - \[Authentication\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication.md): The two headers that authenticate every DCA Partner API request, and the request tiers. - \[Endpoints\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints.md): Every DCA Partner API route — method, auth tier, request, and response. - \[Order & Execution Schema\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema.md): Field-by-field reference for the Order, Execution, and balance-row objects. - \[Error Codes\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes.md): Every DCA Partner API error code, grouped by category, with what triggers it. - \[Limits & Idempotency\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits.md): Row caps, TTLs, per-cycle minimums, and the idempotency contract. - \[Overview\](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview.md): On-chain limit orders with partial fills, searcher execution, and configurable time-in-force. - \[Placing Taker Orders\](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order.md): Fill limit orders as a searcher — instruction layout, accounts, WSOL edge cases, and full execution code. - \[Limit Order Events\](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/events.md): Parse limit order events from program logs using Borsh deserialization. - \[Error Codes\](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/error-codes.md): Custom error codes thrown by the Titan Limit Order program. - \[SDK Reference\](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk.md): Official SDKs for Titan Direct — TypeScript (high-level client) and Rust (types + codec). - \[Community & Support\](https://titan-exchange.gitbook.io/titan/developer-doc/resources/community-and-support.md): Get help, report issues, and connect with the Titan community. - \[AI / LLM Integration\](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration.md): Integrate Titan swap functionality into AI agents and LLM-powered applications. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/getting-started/quickstart-1.md). # Prime Mode and Custom Settings ### Extra User Parameters ![](https://titan-exchange.gitbook.io/files/hsQEnpkNVz68QNGDRoJM) In the settings button at the top right of the swap box, users can configure their transactions and routes in a variety of ways including: \* Prime vs Manual Mode \* Max Slippage \* Transaction Fee Methods \* AMM Exclusion ### Prime Mode Titan's Prime Mode automatically optimizes your swap settings — including slippage and transaction landing — to deliver the best execution through Titan’s meta-aggregator. With automatic sandwich protection, Titan Prime charges zero fees as comparable to other services that will take up to a 10 basis point fee. Along with Titan's propietary algorithm, this means users can get an extra 20 basis points per trade when using Titan Prime. ![](https://titan-exchange.gitbook.io/files/hsQEnpkNVz68QNGDRoJM) When the settings button is showing an animated Prime button, Titan Prime settings will be applied to the trade. {% hint style="info" %} For Titan quotes, the transaction is simulated to see what the user would get at that point in time when the swap button is pressed. If this would result in a revert due to slippage tolerances being exceeded, the attempted swap would result in an error message. {% endhint %} ### Manual Mode #### Max Slippage ![](https://titan-exchange.gitbook.io/files/ZQtJh4TYJBrtSosnkYMR) Slippage is the amount that your final trade differs by from your quoted price when executing a trade. This could be due to several reasons, but the most common is that someone has already traded ahead of you. This setting exists so that if the slippage exceeds your max tolerance, the trade is reverted even though the transaction fee is still taken by the blockchain. There are 2 slippage settings that the user can modify: \* Base Tokens: Any token pair that is not a stablecoin to stablecoin pair or SOL/LST or LST/SOL pair. \* Stable/LST: Any token pair that is a stablecoin to stablecoin pair or SOL/LST or LST/SOL pair These 2 different settings are used to reflect that some tokens are very closely related in value and therefore have far less slippage involved. #### Transaction Fee Methods Titan currently supports two types of broadcast mode fees in order to process transactions with plans to add more soon. These currently are: \* Priority Fees () \* MEV Protect, which is a combo of Jito () and Nozomi () along with anti sandwich protection ![](https://titan-exchange.gitbook.io/files/FvyHYDehL3epAxppxBvU) All broadcast modes have the same options included. With Auto fee mode, Titan determines the appropriate fee level to be paid leveraging 3rd party services such as Helius, Triton, and Jito depending on how fast the user wishes to process the transaction. A max cap on the fee is also set here in case the suggested fee goes above what the user is comfortable paying. ![](https://titan-exchange.gitbook.io/files/lgDcJKdeGeoOKv0nZ4wp) With Custom fee mode, the user sets the exact fee they wish to pay. {% hint style="warning" %} Some wallets may enforce a minimum priority fee to be paid per transaction. {% endhint %} #### AMM Exclusion Users also have the option to exclude various AMMs from the chosen routes for whatever reason. ![](https://titan-exchange.gitbook.io/files/7a5b1UNJFPMbGRw5BmVE) The selected AMMs will then be excluded from generated routes. AMMs have been normalized across DEX Aggregators so that the user does not need to know how each DEX Aggregator handles the naming and identification of various DEXes. --- # Analytics | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/getting-started/analytics.md) . Titan will provide extra analytics to users to help them process their swap information. As we continue to process more trades, we will launch more features that users demand to give them access to timely information about their on-chain trades. After doing a trade, users will be able to see the total outperformance as well as total fee savings that they have obtained from using Titan. This in total makes up the total trading edge that a user gets, meaning the total dollar value that a trader has gained just from using Titan. Outperformance refers to the benefit the trader gets from being able to compare a variety of quotes for their desired trade, especially with quotes unique to Titan. Total fee savings totals up the fees that a trader would have had to pay on other platforms if they had traded there. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252F0Hw9lVa1sXNGyvatMKz9%252FScreen%2520Shot%25202025-09-12%2520at%25206.22.23%2520PM.png%3Falt%3Dmedia%26token%3D5fa7fd83-d457-4475-adfe-6d4f3f319151&width=768&dpr=3&quality=100&sign=e28dcbc&sv=2) Additional statistics such as total volume and the number of trades will also be displayed. In addition, once you have referred 2 or more people, you can see an aggregate view of your referred group's data as well. In addition, the badges that have been earned will also display here. [PreviousTitan Private Swaps](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps) [NextUsernames and Referrals](https://titan-exchange.gitbook.io/titan/getting-started/usernames-and-referrals) Last updated 10 months ago --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/getting-started/titan-dex-integrations.md). # Titan DEX Integrations If you are interested in integrating your AMM or liquidity source into Titan, please see our guidelines at . We handle all types of integrations, from traditional swaps to specialized mint/redeem integrations. If you have questions or a request, please reach out directly to the team on telegram or discord. --- # Titan Private Swaps | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps.md) . Titan Private Swaps add a private execution layer to your token swaps on Solana. Trade with confidence knowing your activity isn't publicly tied to your main wallet history. Each swap is routed through a one-time intermediary wallet generated fresh per trade. This one-time wallet is the on-chain signer — not your primary address. This breaks the on-chain link between your trading activity and your main wallet ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FdlFFNm198hdiAo8krec2%252Fimage.png%3Falt%3Dmedia%26token%3D859d2986-618f-4703-93ae-94fe9d60255d&width=768&dpr=3&quality=100&sign=c2f5869c&sv=2) ### How It's Different[](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps#how-its-different) Regular swaps on Solana are fully public — anyone can trace your wallet address and see every trade you've made. Titan Private Swaps change this by routing each trade through a one-time intermediary wallet generated fresh per swap. This one-time wallet is the on-chain signer, not your primary address. Chain analysis tools cannot link the trade back to you. This is powered by the **Vanish wallet** — a separate wallet generated for your private swaps. Funds sit in your Vanish wallet and are used to execute trades, while your main wallet stays untouched and disconnected from your trading activity. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252F8F8APjlxO5iMujS4nLA6%252Fimage.png%3Falt%3Dmedia%26token%3D160d01ff-68f9-4b7a-9328-7e73a2ce4736&width=768&dpr=3&quality=100&sign=c9093482&sv=2) * **Unlinked activity** — Your main wallet never appears as the trader in any swap transaction. On-chain, only a one-time intermediary wallet is visible, and that wallet is discarded after each trade — making it extremely difficult to build a picture of your trading activity. * **Private trading** — Every swap is routed through a fresh intermediary address that is generated specifically for that trade and never reused. This means no single address accumulates your trading history or token balances, and your activity can't be traced back to a single source. * **Full control, anytime** — Your funds are always in your custody. Deposits are constructed and signed by your own wallet, and you can move funds back to your main wallet at any time with no protocol fees. Vanish can be used or skipped entirely — it's your choice on every swap. * * * ### Getting to Private Swaps[](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps#getting-to-private-swaps) On the swap page, click the **Private** tab in the navigation bar alongside Instant and Limit. If this is your first time, you will be walked through a quick one-time setup to get your Vanish wallet ready. Once set up, the full swap interface loads automatically every time you navigate to the Private tab. #### Enable Vanish[](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps#enable-vanish) If you haven't set up your Vanish wallet yet, you'll land on the onboarding screen first. Click **Enable Vanish** to generate your Vanish wallet. This is a one-time step. Once enabled, you'll be prompted to sign a message with your wallet to authenticate your Vanish session. This signature is verified server-side and stored securely — your API key is never exposed to the browser. Sessions are valid for 24 hours. You'll receive a warning 5 minutes before your session expires, and can re-sign to continue without any disruption. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FnZ0qnGEP6sCLXUzYDNBx%252Fimage.png%3Falt%3Dmedia%26token%3D3fafb145-905c-464e-b33f-50e8f5a463d2&width=768&dpr=3&quality=100&sign=f55dc372&sv=2) #### Fund Your Vanish Wallet[](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps#fund-your-vanish-wallet) Once your Vanish wallet is active, you'll need to transfer funds into it before you can swap. Click **Fund From Main Wallet** on the Private swap page. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FRefCvR9CSlTvpKmZ0rKy%252Fimage.png%3Falt%3Dmedia%26token%3D3d1485d8-ddea-45c1-9044-5d31a776436e&width=768&dpr=3&quality=100&sign=3640e77c&sv=2) A modal will appear showing your **Main wallet** and **Vanish wallet** side by side with their current balances. Enter the amount of SOL you want to transfer and confirm. The transfer transaction is built and signed entirely client-side by your own wallet — Titan never takes custody of your funds during this step. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FZynZUFcGJvUIScC4kSZh%252Fimage.png%3Falt%3Dmedia%26token%3D04c79bc6-617e-4655-9bd3-333be039dab7&width=768&dpr=3&quality=100&sign=a6a5e2e0&sv=2) There are no Protocol fees for this transfer. **Minimum SOL balance** — Your Vanish wallet must always hold at least ~0.012 SOL. This covers the account rent that Vanish loans per trade to set up the one-time intermediary wallet. This minimum is required on top of any SOL you intend to sell. If your balance drops below this threshold, the Swap button will show "Insufficient SOL For Private Swap" and you'll need to top up before continuing. * * * ### Executing a Swap[](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps#executing-a-swap) With your Vanish wallet funded, you're ready to go. The swap interface works similarly to Instant swaps, with a few things specific to Private Swaps to keep in mind. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FyN3uvNYHwZWxgNtt7xRY%252Fimage.png%3Falt%3Dmedia%26token%3Dbce125cb-73e0-456c-a75e-50b34b20b821&width=768&dpr=3&quality=100&sign=69e52c58&sv=2) **Sell** — Select the token and amount you want to sell. The balance shown is your Vanish wallet balance, not your main wallet. **Buy** — Select the token you want to receive. **SOL pairing** — SOL must be on one side of every Private Swap — either the token you're selling or the token you're buying. This is a protocol requirement tied to how the one-time wallet mechanism works. Both SPL tokens and Token-2022 tokens are supported on the non-SOL side. Once you've set your tokens and amounts, review the **Summary** panel before confirming: * **You sell** — the exact amount leaving your Vanish wallet * **You buy (est.)** — the estimated amount you will receive * **Total Vanish points** — points earned from this swap * Protocol **fee** — 0.25% The **Quotes** section shows how Titan's price compares against other aggregators in real time. Titan will always highlight the best available price across routes. **Signing** — Each swap requires a separate wallet signature before it executes. This is by design. Every signature is cryptographically bound to the exact parameters of that trade — token pair, amount, and timestamp — so nothing can be replayed or modified after you sign. Expect one signing prompt per swap. Once signed, click **Swap** to execute. All Private Swaps are submitted via Jito bundles and broadcast in MEV-protected mode, protecting your trade from front-running. **After execution** — There is a short finalization step after the on-chain transaction confirms, where Titan settles the trade internally and updates your balance and trade history. This typically completes quickly but can take up to 2 minutes. If finalization doesn't complete within your session, Titan will automatically pick it up and retry the next time you authenticate. * * * ### What Happens if a Swap Fails?[](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps#what-happens-if-a-swap-fails) Occasionally a Private Swap may not go through — this can happen due to slippage limits being exceeded, network congestion, or the Jito bundle not landing in time. If this happens, your funds are not lost. Since the swap is routed through a one-time intermediary wallet, any funds that were staged for the trade will be returned to your Vanish wallet automatically. This typically happens within a few minutes. You do not need to take any action — the process is handled by Titan in the background. Once your balance is restored you'll see it reflected in your Vanish wallet and can retry the swap. If funds don't appear after a few minutes, check your order history for the status of the failed trade or reach out to the team. * * * ### Managing Your Vanish Wallet[](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps#managing-your-vanish-wallet) You can move funds between your main wallet and Vanish wallet freely at any time in either direction. Click **Move funds** at the bottom of the Private swap page. There are no fees for transfers in either direction. Withdrawals back to your main wallet are handled by a Vanish-constructed transaction that you sign and submit — your destination address is always your own wallet. Your current Vanish wallet balance is always visible on the swap interface so you know exactly what's available to trade. * * * ### Analytics / Points[](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps#analytics-points) Vanish points are earned on every Private Swap and tracked per trade. Your points balance and swap history will be reflected on your profile page and the rewards leaderboard. You can also track individual swap progress from your order history. [PreviousTitan Limit Orders](https://titan-exchange.gitbook.io/titan/getting-started/titan-limit-orders) [NextAnalytics](https://titan-exchange.gitbook.io/titan/getting-started/analytics) Last updated 4 months ago * [How It's Different](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps#how-its-different) * [Getting to Private Swaps](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps#getting-to-private-swaps) * [Executing a Swap](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps#executing-a-swap) * [What Happens if a Swap Fails?](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps#what-happens-if-a-swap-fails) * [Managing Your Vanish Wallet](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps#managing-your-vanish-wallet) * [Analytics / Points](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps#analytics-points) --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/getting-started/quickstart.md). # Quickstart {% hint style="info" %} Titan does not charge any fees on basic swaps. The underlying DEXes or DEX Aggregators that have been incorporated may charge fees {% endhint %} To start interacting with the platform, please connect your wallet. There is a large selection of wallets that you may choose from. ![](https://titan-exchange.gitbook.io/files/ThvWsLQVMyj18n5Wolyl) Once connected, you are able to interact with the swap interface. You can also see the cumulative total of your trading volume with Titan, as well as your total outperformance and edge gained as a result of using Titan over other platforms. {% hint style="info" %} Please note that ledger wallet integrations are supported with these wallets. {% endhint %} ### How to do a Swap You must first select your input and output tokens that you want to trade. The input tokens are sorted based on the USD value of the tokens in your wallet, while the output tokens are shown based on popularity and relevance. Once the tokens are selected, you can select the input token amount you want to trade and quotes will populate to fill in the estimated amount out. Please note that specifying the amount out as of this time is not allowed. You will receive quotes from multiple aggregators. Titan will populate the estimated amount out as well as the relevant transaction details from the best quote. Users can then approve and execute the transaction. Please note that quotes refresh every second. The quotes themselves are simulated live on the blockchain to provide a live stream of the most up to date output given the routes. {% hint style="info" %} Titan provides the best on-chain estimate of the actual output of a given quote. {% endhint %} ![](https://titan-exchange.gitbook.io/files/rBNKUHg5pw1UWpCXbi3Y) The number of pools and DEXes involved in the chosen route will also be shown. Users can then click on the Swap button to execute through their connected wallet application. ### Trade Settings In addition, trades can be sized quickly by selecting the Max or % buttons in the input bar. The % button allows users to have fine grain control over the amount of the input tokens they want to trade. ![](https://titan-exchange.gitbook.io/files/9J6jm1PrrYF1yBcx7Jk9) Users can further customize their settings as described in the Custom Settings page. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/getting-started/publish-your-docs.md). # Private Beta As Titan gets ready to invite all users onboard, a select group of users will be onboarded onto a private beta. This beta is necessary to facilitate user feedback, make optimizations, and ensure that the required infrastructure is set up to support user demand. To sign up for the private beta, please visit where an email can be submitted to the private beta. ![](https://titan-exchange.gitbook.io/files/WUTsJUBMYLY6AKQAJTkY) After submission, please sit back and wait for a confirmation email that you have been enrolled into the Titan Private Beta. ### Invite Codes An invite code will be provided to you through email. To access the beta, please go to \[https://app.titan.exchange\](https://app.titan.exchange/swap). There you will be asked to connect your wallet. ![](https://titan-exchange.gitbook.io/files/TIL1WHSv3c0DIey58Zrg) After you have connected your wallet, the platform will check if that wallet address has already been whitelisted. If not, then a popup appears notifying you to either sign up for the waitlist or proceed with an invite code. If you have an invite code, please click Proceed and go to the next screen. ![](https://titan-exchange.gitbook.io/files/azWj7IfMafdRS3rKyvyj) After entering the invite code, your connected wallet will be whitelisted and you can start to use the Titan platform. {% hint style="info" %} Please note that the wallet you connect will be the only one allowed to access the beta. If there has been a mistake, please submit a support ticket on Titan's discord. {% endhint %} We look forwards to hearing your feedback on Titan! --- # Titan Limit Orders | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/getting-started/titan-limit-orders.md) . Titan Limit Orders are TRUE on-chain limit orders. These are not trigger orders that other platforms may provide; Titan limit orders execute consistently through volatility. Through our unique architecture, Titan is bringing CeFi-grade execution on-chain to Solana, fulfilled by professional market makers like Auros, one of the top market making firms worldwide. Titan Limit Orders are based off of specific token pairs that enable top notch execution quality. We will be adding more pairs as the service keeps on developing. The main issue with Trigger Orders today are that they fill extremely slowly and randomly. Titan Limit Orders fill efficiently and attempts to account for market volatility. The end result are filled orders that can be hundreds and thousands of blocks faster, which is critical for traders. Partial fills are also enabled. A 10 bps fee is charged for Limit Orders with a discount for Titan VIP users. This is lower than comparable products from competitors by up to 50%, while offering better execution quality. There is also a minimum $10 USD notional for this service. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252F23h2zRrGoEE1qnqWMpK3%252FScreen%2520Shot%25202026-01-14%2520at%25209.21.15%2520PM.png%3Falt%3Dmedia%26token%3D30f83553-16f3-460e-9e6b-67853e45cee2&width=768&dpr=3&quality=100&sign=18237b06&sv=2) Once an order expires or is filled, a crank will automatically return funds to the user's main wallet. A rent fee is also collected to open up the relevant account. The full amount is returned upon completion/cancellation of the order. Titan Limit Orders are open to be filled by anyone. Please find instructions here [https://titan-exchange.gitbook.io/titan/titan-developer-docs/apis/searchers-limit-orders](https://titan-exchange.gitbook.io/titan/titan-developer-docs/apis/searchers-limit-orders) and contact the team if there are any questions. ### Limit Order Guide[](https://titan-exchange.gitbook.io/titan/getting-started/titan-limit-orders#limit-order-guide) In order to navigate to Limit Order section, please click the Limit tab on the swap page. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FvF8TNpg9QrjleZywsLjU%252FScreen%2520Shot%25202026-01-14%2520at%25209.24.56%2520PM.png%3Falt%3Dmedia%26token%3Dc00a6581-a671-4463-9656-eb08ad65eef3&width=768&dpr=3&quality=100&sign=d63abbd4&sv=2) Once navigated, users will have chart visualization and order history loaded up as well. Please click on the dropdown to select the token pair you wish to trade. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FyBKkYfOyfbhBOMvdhsq9%252FScreen%2520Shot%25202026-01-14%2520at%25209.30.31%2520PM.png%3Falt%3Dmedia%26token%3D71484550-f813-4a15-88f7-ff1ba55e020c&width=768&dpr=3&quality=100&sign=ad41275b&sv=2) After the trading pair is selected, please choose the direction of the trade, the limit price, quantity, and expiry date. After these are chosen, please execute the order. As a reminder, users will not be able to set a limit price that is above the market price. A minimum $10 notional is also required. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FlgSZ5OnDoTdw3Gf2eeVf%252FScreen%2520Shot%25202026-01-14%2520at%25209.30.47%2520PM.png%3Falt%3Dmedia%26token%3Dc2d69b94-1889-4ff1-808d-f15d141a8ede&width=768&dpr=3&quality=100&sign=9e783a56&sv=2) ### Order Management[](https://titan-exchange.gitbook.io/titan/getting-started/titan-limit-orders#order-management) You can manage your orders directly from the swap page under open orders. The progress of the order, expiry, and limit price can be seen. In addition, there is also a link out to solscan as well as the ability to cancel an order if you click on the x. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FipINEENYWNsPa9blaZVc%252FScreen%2520Shot%25202026-01-14%2520at%25209.34.04%2520PM.png%3Falt%3Dmedia%26token%3Df7bdffd1-009c-482a-989a-f93d9a5de256&width=768&dpr=3&quality=100&sign=71bf0d5c&sv=2) You can also navigate to order history to check the progress. If you click on an order, you will be able to check the history for that particular order. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FtWbvJCMoqHnCE17WsGvE%252FScreen%2520Shot%25202026-01-14%2520at%25209.35.38%2520PM.png%3Falt%3Dmedia%26token%3D5e8db72b-7b2b-49de-b1d9-3018de1f3e2d&width=768&dpr=3&quality=100&sign=1ca2753f&sv=2) In addition, there is also a notification button that will notify you every time a limit order has an update. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FErTGwIPlPtBQL01wJzny%252FScreen%2520Shot%25202026-01-14%2520at%25209.36.39%2520PM.png%3Falt%3Dmedia%26token%3D86bf5844-54eb-480c-a40b-f1dfbff61f17&width=768&dpr=3&quality=100&sign=85e28a3b&sv=2) ### Analytics[](https://titan-exchange.gitbook.io/titan/getting-started/titan-limit-orders#analytics) Limit Orderes will have its own tracking in the summary page as well as the profile page. There will also be a dedicated limit order leaderboard. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FHSQusodFBQcRBdUaS4vA%252FScreen%2520Shot%25202026-01-14%2520at%25209.37.15%2520PM.png%3Falt%3Dmedia%26token%3De0f7f40b-dbc2-4b71-b942-799149677cba&width=768&dpr=3&quality=100&sign=b9a2e9df&sv=2) ### Charting Data[](https://titan-exchange.gitbook.io/titan/getting-started/titan-limit-orders#charting-data) The chart data comes from a combination of Trading View and Binance. This may not be reflective of a market maker's inventory at certain times. Please be aware of this. [PreviousPrime Mode and Custom Settings](https://titan-exchange.gitbook.io/titan/getting-started/quickstart-1) [NextTitan Private Swaps](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps) Last updated 6 months ago * [Limit Order Guide](https://titan-exchange.gitbook.io/titan/getting-started/titan-limit-orders#limit-order-guide) * [Order Management](https://titan-exchange.gitbook.io/titan/getting-started/titan-limit-orders#order-management) * [Analytics](https://titan-exchange.gitbook.io/titan/getting-started/titan-limit-orders#analytics) * [Charting Data](https://titan-exchange.gitbook.io/titan/getting-started/titan-limit-orders#charting-data) --- # Usernames and Referrals | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/getting-started/usernames-and-referrals.md) . Titan will also allow you set your username that can be used to represent your profile. This username will also be used to refer other users to access the platform by giving them invite codes. Users who refer others will be able to get a view of the total aggregate volume and outperformance from themselves as well as their referred users. Referral invite codes can be found in the wallet panel at the top right of the panel ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252Fem4osBWHX3mHuyy7W3oe%252FScreen%2520Shot%25202025-08-13%2520at%25201.38.47%2520PM.png%3Falt%3Dmedia%26token%3D91c725c0-91fe-4e0f-a2ae-695114cb4529&width=768&dpr=3&quality=100&sign=a21a6145&sv=2) There may be additional benefits for both the referred and the referrer in the future. [PreviousAnalytics](https://titan-exchange.gitbook.io/titan/getting-started/analytics) [NextTitan DEX Integrations](https://titan-exchange.gitbook.io/titan/getting-started/titan-dex-integrations) Last updated 10 months ago --- # How Swaps Work | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/how-swaps-work.md) . A swap is how users on a blockchain trade one token for another. A straight token to token trade without the use of leverage is called a spot swap/trade as the underlying token is the one being traded. In DeFi, a user signs a transaction through their wallet for their trade to execute on the desired platform. This transaction propagates throughout the entire blockchain worldwide and after a short delay, the trade is confirmed and you have the results of your trade in your wallet. An advantage here is that the funds are transferred immediately without waiting for the lag time that is present in traditional markets. In addition, Solana does this at very low costs (sub cent), thus making it more efficient than many money transmitting networks, especially internationally. [PreviousTitan DART](https://titan-exchange.gitbook.io/titan/getting-started/titan-dart) [NextDEX Aggregators](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/dex-aggregators) Last updated 5 months ago --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/getting-started/titan-limit-orders.md). # Titan Limit Orders Titan Limit Orders are TRUE on-chain limit orders. These are not trigger orders that other platforms may provide; Titan limit orders execute consistently through volatility. Through our unique architecture, Titan is bringing CeFi-grade execution on-chain to Solana, fulfilled by professional market makers like Auros, one of the top market making firms worldwide. Titan Limit Orders are based off of specific token pairs that enable top notch execution quality. We will be adding more pairs as the service keeps on developing. The main issue with Trigger Orders today are that they fill extremely slowly and randomly. Titan Limit Orders fill efficiently and attempts to account for market volatility. The end result are filled orders that can be hundreds and thousands of blocks faster, which is critical for traders. Partial fills are also enabled. A 10 bps fee is charged for Limit Orders with a discount for Titan VIP users. This is lower than comparable products from competitors by up to 50%, while offering better execution quality. There is also a minimum $10 USD notional for this service. ![](https://titan-exchange.gitbook.io/files/hDy186PNfy6I2xYb51kQ) Once an order expires or is filled, a crank will automatically return funds to the user's main wallet. {% hint style="info" %} A rent fee is also collected to open up the relevant account. The full amount is returned upon completion/cancellation of the order. {% endhint %} Titan Limit Orders are open to be filled by anyone. Please find instructions here and contact the team if there are any questions. ### Limit Order Guide In order to navigate to Limit Order section, please click the Limit tab on the swap page. ![](https://titan-exchange.gitbook.io/files/EiofaUjM8fhavy7VCTah) Once navigated, users will have chart visualization and order history loaded up as well. Please click on the dropdown to select the token pair you wish to trade. ![](https://titan-exchange.gitbook.io/files/cxQOCW6s9HJVgA0QBekG) After the trading pair is selected, please choose the direction of the trade, the limit price, quantity, and expiry date. After these are chosen, please execute the order. As a reminder, users will not be able to set a limit price that is above the market price. A minimum $10 notional is also required. ![](https://titan-exchange.gitbook.io/files/qW3Ub2l0e6PGfuPhgBQp) \### Order Management You can manage your orders directly from the swap page under open orders. The progress of the order, expiry, and limit price can be seen. In addition, there is also a link out to solscan as well as the ability to cancel an order if you click on the x. ![](https://titan-exchange.gitbook.io/files/y51HWt1ypxywfYIM8g7X) You can also navigate to order history to check the progress. If you click on an order, you will be able to check the history for that particular order. ![](https://titan-exchange.gitbook.io/files/nsU4VdT7YLHHDz6AN2iW) In addition, there is also a notification button that will notify you every time a limit order has an update. ![](https://titan-exchange.gitbook.io/files/xQkc6iPTc7A56CWx4GfM) \### Analytics Limit Orderes will have its own tracking in the summary page as well as the profile page. There will also be a dedicated limit order leaderboard. ![](https://titan-exchange.gitbook.io/files/xlI99bPukpXGPLfxAnKF) \### Charting Data The chart data comes from a combination of Trading View and Binance. This may not be reflective of a market maker's inventory at certain times. Please be aware of this. --- # Titan's Unique Algorithm | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/editor.md) . Historically, DEX aggregators have relied on shortest path algorithms to determine routes between liquidity. In doing so, they have benefited from battle tested algorithms that are simple to implement. However, given the nature of the crypto markets, latency requirements and underlying assumptions of shortest path algorithms, liquidity sources are often temporarily removed when these types of algorithms are used in order to make routing possible. In addition to this, a specific route in a network may not have significant capacity. If the pools involved have a small TVL then the price can change rapidly as more of the users’ funds are swapped through each relevant exchange. To capture the effects of price impact, the liquidity is frequently fragmented into pieces and each pool is broken into many parts with an average price and a certain capacity. This is frequently reflected both in the algorithm and its outputs. For example, currently Jupiter, another DEX aggregator, generally chunks their routes into neat 1% buckets. This fragmentation of liquidity increases the size of the network being searched and leads to inaccuracies when the pool is poorly resolved. These two challenges are among the main obstacles in applying shortest path methods for DEX aggregation. To address them, Titan leverages a different set of algorithms that focus on optimization, which resulted in the development of the Argos algorithm. These algorithms can efficiently resolve price impacts with machine-level precision without fragmenting pools and eliminates the need to exclude various liquidity sources, thus leading to a true optimal on-chain price. [PreviousDEX Aggregators](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/dex-aggregators) [NextMeta Aggregation](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/markdown) Last updated 4 months ago --- # DEX Aggregators | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/dex-aggregators.md) . There are multiple ways to do spot trades on Solana. You can either trade directly with a centralized exchange (CEX) such as Binance or Coinbase, a decentralized exchange (DEX) such as Orca or Raydium, or a DEX Aggregator like Titan or Jupiter. If you trade through a CEX/DEX, you are only sourcing liquidity for your trade from one venue, which may not offer the best price. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FnjSSCrEKB7vODKKmryZn%252FTitan%2520Argos.jpg%3Falt%3Dmedia%26token%3D91aa70cb-d8ea-499e-a9c8-30dc3c4b9770&width=768&dpr=3&quality=100&sign=e80e361a&sv=2) In traditional finance markets, brokers are connected to multiple sources of liquidity to source you the best price if you have an account. In crypto, the only way to do this without dedicated infrastructure and multiple compliance checks is to aggregate the liquidity among DEXes. Platforms that offer these are called DEX Aggregators. The problem that DEX Aggregators face are fairly unique. In traditional markets, the speed is so fast (nanoseconds) that order flow must be processed almost instantly, but in crypto markets, there is enough distributed liquidity and enough time to deploy advanced analytics to find the best possible route. DEX Aggregation on Solana is especially strong due to the network's low fees. This makes it feasible to find complex routes that provide better prices without it being absorbed by extremely high gas prices as in Ethereum. Due to this, DEX Aggregators are the preferred way for users to trade on low cost chains in order to maximize their asset's value. [PreviousHow Swaps Work](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/how-swaps-work) [NextTitan's Unique Algorithm](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/editor) Last updated 11 months ago --- # Meta Aggregation | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/markdown.md) . DEX Aggregation is the way to go for low cost chains, but every DEX Aggregator provides different quotes. This is due to different algorithms being used, as well as different data sets. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FPuVUTFjGnrpv8HEVZlfv%252F2.jpg%3Falt%3Dmedia%26token%3Dcbeb8631-1eb1-4fc7-beaa-75f7e2143f1a&width=768&dpr=3&quality=100&sign=d33d5743&sv=2) Regardless of the technique being used or aggregator in question, Titan will combine DEX Aggregators for users, thus becoming a Meta Aggregator. A Meta Aggregator utilises quotes from each individual Aggregator and then provides the best quote to the end user. This way, the user can be confident that they will always receive the best price on offer at any time. This would be very similar to your traditional broker in equity markets. When an order is placed, the broker would contact different market makers who would supply the liquidity. These market makers would make the trades on the individual exchanges. The broker would then select the best quote and send it to the user. In this scenario, the players translated over to the crypto ecosystem would be: * Exchange -> DEX * Market Maker -> DEX Aggregator * Broker -> Meta Aggregator Titan is sitting at this Meta Aggregator level to guarantee users the best price possible. #### How to Compare and Verify[](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/markdown#how-to-compare-and-verify) In order for Meta Aggregation to work, aggregator quotes have to be accurate, as well as be on the same block for them to be comparable. We have to be able to weed out inaccurate quotes as well as compensate for latency factors. Thankfully the solution for both problems is the same. Titan simulates all quotes directly on the blockchain. This shows the real amount out that a user would get if executed at that point in time. This also allows quotes to be compared as it removes the latency impacts of quotes arriving at different times. A given quote is simulated throughout its valid period to provide users the most up to date information. [PreviousTitan's Unique Algorithm](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/editor) [NextDART Routing](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/dart-routing) Last updated 11 months ago --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/getting-started/analytics.md). # Analytics Titan will provide extra analytics to users to help them process their swap information. As we continue to process more trades, we will launch more features that users demand to give them access to timely information about their on-chain trades. After doing a trade, users will be able to see the total outperformance as well as total fee savings that they have obtained from using Titan. This in total makes up the total trading edge that a user gets, meaning the total dollar value that a trader has gained just from using Titan. Outperformance refers to the benefit the trader gets from being able to compare a variety of quotes for their desired trade, especially with quotes unique to Titan. Total fee savings totals up the fees that a trader would have had to pay on other platforms if they had traded there.
Additional statistics such as total volume and the number of trades will also be displayed. In addition, once you have referred 2 or more people, you can see an aggregate view of your referred group's data as well. In addition, the badges that have been earned will also display here. --- # Titan DART | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/getting-started/titan-dart.md) . DART Settings[](https://titan-exchange.gitbook.io/titan/getting-started/titan-dart#dart-settings) -------------------------------------------------------------------------------------------------- Control how Titan routes your swaps — configure DART directly from the swap interface. ### Configuration[](https://titan-exchange.gitbook.io/titan/getting-started/titan-dart#configuration) Above the swap box, users can configure their DART settings by clicking on the DART box. For DART eligible pairs, DART defaults to **Auto**. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FpqpForzkDsTtEF9eglWu%252Fimage.png%3Falt%3Dmedia%26token%3Df239cf5b-66b0-4af9-8839-3aa00bc9460d&width=768&dpr=3&quality=100&sign=8b169b1b&sv=2) Mode Behavior **Auto** Titan uses a combination of both Argos and DART to determine the optimal execution. Recommended setting. **Only** Titan only uses DART routing. **Off** Titan only will use off-chain Argos routing. ### DART Available Pairs[](https://titan-exchange.gitbook.io/titan/getting-started/titan-dart#dart-available-pairs) DART routing is currently available for the following pairs, with more coming online soon. * SOL/USDC * SOL/USDT * USDT/USDC * cbBTC/USDC * wETH/USDC * TRUMP/USDC * ZEC/USDC * USD1/USDC * HYPE/USDC * PUMP/USDC * PENGU/USDC * FARTCOIN/USDC * syrupUSD/USDC * PYUSD/USDC * USDG/USDC * CASH/USDC * AAVE/USDC * MEGA/USDC * SPCX/USDC * MU/USDC ### DART Fees[](https://titan-exchange.gitbook.io/titan/getting-started/titan-dart#dart-fees) Titan DART charges up to maximum 1 bps per swap. Users on Titan only get routed via DART if the end execution including fees and expected slippage is better than all other routers. ### DART Status Indicator[](https://titan-exchange.gitbook.io/titan/getting-started/titan-dart#dart-status-indicator) The colored dot on the DART swap page is a visual indicator that tells you the live execution status of DART at any moment. It stays static with no animation or flickering for clarity. ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FovdVS5mfp3dPbA55PwN7%252Fimage.png%3Falt%3Dmedia%26token%3De0ab4da4-5b23-4916-a2a0-326db95474d5&width=768&dpr=3&quality=100&sign=dfa90240&sv=2) DART set to **Only** with the green status indicator confirming DART is actively executing the swap. Titan returns the best price across all quoted routes ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FX1NGA0trOgRpIpj94ftC%252Fimage.png%3Falt%3Dmedia%26token%3D0e402374-413f-48aa-9973-e313c1224c4e&width=768&dpr=3&quality=100&sign=c0f3da5d&sv=2) DART is set to **Off** — the red indicator confirms your transaction will be executed via non-DART routes Color Meaning **Green** DART is live and active — it is the selected route that will actually execute your swap. **Red** DART is not executing the trade — either it's not available, or another route is being used instead. > **Note:** The dot does not turn green just because DART shows up in the quotes list. It only turns green when DART is the winning/selected quote that will be executed. Green truly means DART is handling your trade, not just participating in the comparison. A single glance tells you whether you're getting the DART execution experience or not, without needing to dig into route details. [PreviousTitan DEX Integrations](https://titan-exchange.gitbook.io/titan/getting-started/titan-dex-integrations) [NextHow Swaps Work](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/how-swaps-work) Last updated 1 month ago * [DART Settings](https://titan-exchange.gitbook.io/titan/getting-started/titan-dart#dart-settings) * [Configuration](https://titan-exchange.gitbook.io/titan/getting-started/titan-dart#configuration) * [DART Available Pairs](https://titan-exchange.gitbook.io/titan/getting-started/titan-dart#dart-available-pairs) * [DART Fees](https://titan-exchange.gitbook.io/titan/getting-started/titan-dart#dart-fees) * [DART Status Indicator](https://titan-exchange.gitbook.io/titan/getting-started/titan-dart#dart-status-indicator) --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/getting-started/titan-private-swaps.md). # Titan Private Swaps Titan Private Swaps add a private execution layer to your token swaps on Solana. Trade with confidence knowing your activity isn't publicly tied to your main wallet history. Each swap is routed through a one-time intermediary wallet generated fresh per trade. This one-time wallet is the on-chain signer — not your primary address. This breaks the on-chain link between your trading activity and your main wallet
### How It's Different Regular swaps on Solana are fully public — anyone can trace your wallet address and see every trade you've made. Titan Private Swaps change this by routing each trade through a one-time intermediary wallet generated fresh per swap. This one-time wallet is the on-chain signer, not your primary address. Chain analysis tools cannot link the trade back to you. This is powered by the \*\*Vanish wallet\*\* — a separate wallet generated for your private swaps. Funds sit in your Vanish wallet and are used to execute trades, while your main wallet stays untouched and disconnected from your trading activity.
\* \*\*Unlinked activity\*\* — Your main wallet never appears as the trader in any swap transaction. On-chain, only a one-time intermediary wallet is visible, and that wallet is discarded after each trade — making it extremely difficult to build a picture of your trading activity. \* \*\*Private trading\*\* — Every swap is routed through a fresh intermediary address that is generated specifically for that trade and never reused. This means no single address accumulates your trading history or token balances, and your activity can't be traced back to a single source. \* \*\*Full control, anytime\*\* — Your funds are always in your custody. Deposits are constructed and signed by your own wallet, and you can move funds back to your main wallet at any time with no protocol fees. Vanish can be used or skipped entirely — it's your choice on every swap. \*\*\* ### Getting to Private Swaps On the swap page, click the \*\*Private\*\* tab in the navigation bar alongside Instant and Limit. If this is your first time, you will be walked through a quick one-time setup to get your Vanish wallet ready. Once set up, the full swap interface loads automatically every time you navigate to the Private tab. #### Enable Vanish If you haven't set up your Vanish wallet yet, you'll land on the onboarding screen first. Click \*\*Enable Vanish\*\* to generate your Vanish wallet. This is a one-time step. Once enabled, you'll be prompted to sign a message with your wallet to authenticate your Vanish session. This signature is verified server-side and stored securely — your API key is never exposed to the browser. Sessions are valid for 24 hours. You'll receive a warning 5 minutes before your session expires, and can re-sign to continue without any disruption.
#### Fund Your Vanish Wallet Once your Vanish wallet is active, you'll need to transfer funds into it before you can swap. Click \*\*Fund From Main Wallet\*\* on the Private swap page.
A modal will appear showing your \*\*Main wallet\*\* and \*\*Vanish wallet\*\* side by side with their current balances. Enter the amount of SOL you want to transfer and confirm. The transfer transaction is built and signed entirely client-side by your own wallet — Titan never takes custody of your funds during this step.
There are no Protocol fees for this transfer. {% hint style="info" %} \*\*Minimum SOL balance\*\* — Your Vanish wallet must always hold at least \\~0.012 SOL. This covers the account rent that Vanish loans per trade to set up the one-time intermediary wallet. This minimum is required on top of any SOL you intend to sell. If your balance drops below this threshold, the Swap button will show "Insufficient SOL For Private Swap" and you'll need to top up before continuing. {% endhint %} \*\*\* ### Executing a Swap With your Vanish wallet funded, you're ready to go. The swap interface works similarly to Instant swaps, with a few things specific to Private Swaps to keep in mind.
\*\*Sell\*\* — Select the token and amount you want to sell. The balance shown is your Vanish wallet balance, not your main wallet. \*\*Buy\*\* — Select the token you want to receive. {% hint style="info" %} \*\*SOL pairing\*\* — SOL must be on one side of every Private Swap — either the token you're selling or the token you're buying. This is a protocol requirement tied to how the one-time wallet mechanism works. Both SPL tokens and Token-2022 tokens are supported on the non-SOL side. {% endhint %} Once you've set your tokens and amounts, review the \*\*Summary\*\* panel before confirming: \* \*\*You sell\*\* — the exact amount leaving your Vanish wallet \* \*\*You buy (est.)\*\* — the estimated amount you will receive \* \*\*Total Vanish points\*\* — points earned from this swap \* Protocol \*\*fee\*\* — 0.25% The \*\*Quotes\*\* section shows how Titan's price compares against other aggregators in real time. Titan will always highlight the best available price across routes. \*\*Signing\*\* — Each swap requires a separate wallet signature before it executes. This is by design. Every signature is cryptographically bound to the exact parameters of that trade — token pair, amount, and timestamp — so nothing can be replayed or modified after you sign. Expect one signing prompt per swap. Once signed, click \*\*Swap\*\* to execute. All Private Swaps are submitted via Jito bundles and broadcast in MEV-protected mode, protecting your trade from front-running. \*\*After execution\*\* — There is a short finalization step after the on-chain transaction confirms, where Titan settles the trade internally and updates your balance and trade history. This typically completes quickly but can take up to 2 minutes. If finalization doesn't complete within your session, Titan will automatically pick it up and retry the next time you authenticate. \*\*\* ### What Happens if a Swap Fails? Occasionally a Private Swap may not go through — this can happen due to slippage limits being exceeded, network congestion, or the Jito bundle not landing in time. If this happens, your funds are not lost. Since the swap is routed through a one-time intermediary wallet, any funds that were staged for the trade will be returned to your Vanish wallet automatically. This typically happens within a few minutes. You do not need to take any action — the process is handled by Titan in the background. Once your balance is restored you'll see it reflected in your Vanish wallet and can retry the swap. If funds don't appear after a few minutes, check your order history for the status of the failed trade or reach out to the team. \*\*\* ### Managing Your Vanish Wallet You can move funds between your main wallet and Vanish wallet freely at any time in either direction. Click \*\*Move funds\*\* at the bottom of the Private swap page. There are no fees for transfers in either direction. Withdrawals back to your main wallet are handled by a Vanish-constructed transaction that you sign and submit — your destination address is always your own wallet. Your current Vanish wallet balance is always visible on the swap interface so you know exactly what's available to trade. \*\*\* ### Analytics / Points Vanish points are earned on every Private Swap and tracked per trade. Your points balance and swap history will be reflected on your profile page and the rewards leaderboard. You can also track individual swap progress from your order history. --- # DART Routing | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/dart-routing.md) . **DART** (Dynamic Allocation and Real Time) Routing is Titan's onchain routing engine and the world's first router that dynamically re-optimizes a trade at the exact moment of execution, not seconds before. ### The Quote to Execution Gap[](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/dart-routing#the-quote-to-execution-gap) ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FeWfMEJaN9h06OygNExW8%252Fimage.png%3Falt%3Dmedia%26token%3Dce997a9b-cb57-4c22-aa76-ef61212d49f1&width=768&dpr=3&quality=100&sign=db5491e4&sv=2) Aggregators typically compute the route for a trade before the transaction is submitted to the blockchain. A route is calculated, a quote is returned to the user, and the transaction is then broadcast to the network. By the time that transaction lands in a block, anywhere from hundreds of milliseconds to several seconds may have passed. In that window, liquidity conditions across pools change. Prices shift. Other trades execute against the same routes. The route that was optimal at quote time is no longer optimal at execution time. This gap between when a route is computed and when it actually executes is an inherent limitation of all pre-execution routing systems. For small trades this gap may be small. For larger trades, the cost of this staleness compounds quickly, as the price impact and route quality have both moved in the time it took for the transaction to settle. ### How DART Works[](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/dart-routing#how-dart-works) DART operates on a straightforward principle: at execution time, the combination of pools offering the best available prices for your trade wins the order. Market makers need the flexibility to adjust spreads in either direction when required. DART ensures your trade always selects the best venues, regardless of market maker's adjustments. Before the transaction is built, Titan's offchain infrastructure determines the optimal route shape: which pools to include, which venues have the deepest liquidity for your trade size, and which paths are worth considering. This is the foundation that DART builds on. At execution time, **DART dynamically re-optimizes how your volume is split across a large number of pools in real time**, onchain. It utilizes the full liquidity universe including: ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252FXc2LQcBi4litvQ66kabB%252Fimage.png%3Falt%3Dmedia%26token%3D224aa04f-784d-4b2b-9bdd-f172c46f3f71&width=768&dpr=3&quality=100&sign=8792a9d0&sv=2) ![](https://titan-exchange.gitbook.io/titan/~gitbook/image?url=https%3A%2F%2F214878899-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKEg3FeKkeNIHryTs7Kso%252Fuploads%252F2uLMvrMnSu5qwA7P67CQ%252Fimage.png%3Falt%3Dmedia%26token%3D535bc05a-9746-40a5-ba06-15681bdc0169&width=768&dpr=3&quality=100&sign=534d37d0&sv=2) * Prop AMMs * Non-prop AMMs (Orca, Meteora) * Orderbook-based DEXes * Mint/redeem pools More pools, smarter weight optimization, better execution. Working alongside **Argos**, Titan's offchain router and already the most advanced on Solana, the combination forms a hybrid routing engine. Together, they close the gap as close as possible to deliver the best execution available on Solana today. ### Best Bid Offer Guarantee[](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/dart-routing#best-bid-offer-guarantee) Because DART resolves routing at execution time against actual onchain state, it provides a **Best Bid Offer (BBO)** guarantee. This means users are always filled by the market makers offering the best available quotes at the precise moment their trade executes. In traditional financial markets, BBO is the standard that regulated venues are required to achieve. It means that when you submit an order, the exchange must fill it at the best available price across all connected liquidity sources at that moment. DART brings this same guarantee onchain for the first time on Solana. The result is that users are not just getting the best route that could be found before their transaction was sent. They are getting the best route that exists at the moment their transaction lands, computed in real time from live market conditions. > **DART is the de-facto standard for onchain execution on Solana.** [PreviousMeta Aggregation](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/markdown) Last updated 4 months ago * [The Quote to Execution Gap](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/dart-routing#the-quote-to-execution-gap) * [How DART Works](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/dart-routing#how-dart-works) * [Best Bid Offer Guarantee](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/dart-routing#best-bid-offer-guarantee) --- # Authentication | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/authentication.md) . The Titan API uses **JSON Web Tokens (JWTs)** for authentication. Every connection requires a valid token issued by Titan or a Titan distributor. Submitting your token[](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/authentication#submitting-your-token) ------------------------------------------------------------------------------------------------------------------------------------ You can submit your JWT in two ways: As a **Bearer token** in the `Authorization` header — the recommended approach for server-side integrations: Copy Authorization: Bearer As a query parameter — for browser clients or environments where setting custom headers isn't possible (e.g. WebSocket clients that only support [`Sec-WebSocket-Protocol`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-WebSocket-Protocol) ): Copy wss://YOUR_ENDPOINT/api/v1/ws?auth= https://YOUR_ENDPOINT/api/v1/quote/swap?auth= **Do not expose your JWT in client-side code.** Set up a middleware proxy that injects the token server-side. See the [middleware example](https://github.com/Titan-Pathfinder/titan-sdk-ts/blob/main/examples/middleware.ts) for a basic reference — you can build your own middleware to fit your stack and authentication flow. If your connection drops unexpectedly, check your token's expiration first — an **expired token is the most common cause** of authentication failures. Getting a token[](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/authentication#getting-a-token) ------------------------------------------------------------------------------------------------------------------------ See [Get API Access](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/api-access) . [PreviousIntroduction](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/introduction) [NextGet API Access](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/api-access) Last updated 4 months ago * [Submitting your token](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/authentication#submitting-your-token) * [Getting a token](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/authentication#getting-a-token) --- # Introduction | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/introduction.md) . Titan is a meta-aggregator for Solana. It collects quotes from multiple providers — DEX aggregators and RFQ providers — routes through Argos, and returns the best quotes. What Titan offers[](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/introduction#what-titan-offers) -------------------------------------------------------------------------------------------------------------------------- **Titan Direct** is a WebSocket API that streams live swap quotes, updated continuously as on-chain state changes — **the only WebSocket-native trading API on Solana, built for traders who can't afford stale quotes.** Use it when you need real-time pricing — trading bots, live swap UIs, or any integration where quotes should refresh automatically. **Titan Gateway** is a REST API that returns quotes per request — no persistent connection required. **The fastest path from zero to production-grade swap execution on Solana.** Use it when a single quote per action is enough — one-click swap buttons, price displays, or backend services that request quotes on demand. Both paths deliver the same execution quality through Argos. The difference is interface and workflow — not routing quality. **Limit Orders** lets you place resting on-chain orders that fill fully or partially as liquidity becomes available. The program supports five time-in-force modes and charges fees to takers in the output token. Routing with Argos[](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/introduction#routing-with-argos) ---------------------------------------------------------------------------------------------------------------------------- Every swap request runs through **Argos**, Titan's proprietary routing algorithm. Where competing aggregators rely on shortest-path methods and chunk liquidity into percentage buckets, Argos resolves price impacts with machine-level precision — without fragmenting pools or excluding liquidity sources. The result is a true optimal on-chain price, which is why Titan beats competing aggregators 80% of the time. Where to start?[](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/introduction#where-to-start) --------------------------------------------------------------------------------------------------------------------- Need a token first? Go to [Get API Access](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/api-access) . Ready to build? The [Quickstart](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/quickstart) gets you your first swap quote in minutes using both Titan Direct and Titan Gateway. [PreviousHome](https://titan-exchange.gitbook.io/titan/developer-doc) [NextAuthentication](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/authentication) Last updated 4 months ago * [What Titan offers](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/introduction#what-titan-offers) * [Routing with Argos](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/introduction#routing-with-argos) * [Where to start?](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/introduction#where-to-start) --- # Get API Access | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/api-access.md) . Titan API access is available through infrastructure providers. Through a distributor[](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/api-access#through-a-distributor) -------------------------------------------------------------------------------------------------------------------------------- Titan is available through the following infrastructure providers: * [**Triton**](https://docs.triton.one/trading-apis/titan-swap-api) — Titan Swap API documentation on Triton * [**QuickNode**](https://marketplace.quicknode.com/add-on/titan-swap) — Titan Swap add-on on the QuickNode Marketplace If you're already on either platform, you can get access without contacting the Titan team directly. Once you have a token and endpoint, go to the [Quickstart](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/quickstart) to make your first request. [PreviousAuthentication](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/authentication) [NextQuickstart](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/quickstart) Last updated 6 days ago --- # Guides | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides.md) . **Practical walkthroughs for the most common Swap API workflows.** Each guide builds on the [Quickstart](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/quickstart) and assumes you have a working connection. [PreviousQuickstart](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/quickstart) [NextSwap V2 vs Swap V3](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3) Last updated 4 months ago --- # Quickstart | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/quickstart.md) . You'll need an API token and endpoint URL before starting. See [Get API Access](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/api-access) if you don't have one yet. This is a minimal example to get your first quote from the Titan API. For a complete integration with transaction building, signing, and error handling, see [Stream & Execute a Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute) . Titan has two integration paths. **Titan Direct** uses WebSocket and streams live quotes continuously. **Titan Gateway** uses REST and returns a single set of quotes per request. Both deliver the same quote quality. Swap quote requests require a `userPublicKey` — a valid Solana wallet address. The server uses it to build transaction instructions scoped to that wallet. The [`@titanexchange/sdk-ts`](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk) SDK supports Titan Direct (WebSocket) only. Titan Direct Titan Gateway 1 **Install the SDK** Copy npm install @titanexchange/sdk-ts bs58 2 **Set your credentials** Copy export TITAN_ENDPOINT="wss://YOUR_ENDPOINT/api/v1/ws" export TITAN_API_KEY="YOUR_API_TOKEN" 3 **Connect and get a quote** Copy import { V1Client } from '@titanexchange/sdk-ts'; import bs58 from 'bs58'; const client = await V1Client.connect( `${process.env.TITAN_ENDPOINT}?auth=${process.env.TITAN_API_KEY}` ); const { stream } = await client.newSwapQuoteStream({ swap: { inputMint: bs58.decode('So11111111111111111111111111111111111111112'), outputMint: bs58.decode('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'), amount: BigInt(1_000_000_000), // 1 SOL in lamports slippageBps: 50, }, transaction: { userPublicKey: bs58.decode('YOUR_WALLET_PUBLIC_KEY'), }, }); for await (const update of stream) { const quotes = update.quotes; if (!Object.keys(quotes).length) continue; for (const [provider, route] of Object.entries(quotes as Record)) { console.log(`${provider}: ${route.outAmount} out`); } break; // First update received — stop here } await client.close(); 1 **Install dependencies** Copy npm install @msgpack/msgpack 2 **Set your credentials** Copy export TITAN_ENDPOINT="https://YOUR_ENDPOINT" export TITAN_API_KEY="YOUR_API_TOKEN" 3 **Request a quote** Copy import { decode } from '@msgpack/msgpack'; const params = new URLSearchParams({ inputMint: 'So11111111111111111111111111111111111111112', outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', amount: '1000000000', // 1 SOL in lamports userPublicKey: 'YOUR_WALLET_PUBLIC_KEY', slippageBps: '50', }); const res = await fetch( `${process.env.TITAN_ENDPOINT}/api/v1/quote/swap?${params}`, { headers: { 'Authorization': `Bearer ${process.env.TITAN_API_KEY}`, 'Accept': 'application/vnd.msgpack', }, } ); if (!res.ok) throw new Error(`${res.status}: ${res.statusText}`); const data = decode(new Uint8Array(await res.arrayBuffer())) as any; for (const [provider, route] of Object.entries(data.quotes as Record)) { console.log(`${provider}: ${route.outAmount} out`); } What a quote looks like[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/quickstart#what-a-quote-looks-like) ----------------------------------------------------------------------------------------------------------------------------- The response contains a `quotes` map keyed by provider ID — `"Titan"`, `"Metis"`, `"Okx"`, etc. Each entry is a `SwapRoute` with everything you need to build and send a transaction: * `**inAmount**` / `**outAmount**` — the input and output amounts for this route. * `**slippageBps**` — the slippage tolerance applied to this quote. * `**computeUnitsSafe**` — recommended compute budget that accounts for on-chain variance. * `**instructions**` — the swap instructions to include in your transaction. * `**addressLookupTables**` — ALT addresses needed to compile a V0 transaction. * **Quote expiry** — if a route expires, `expiresAtMs` contains the expiry as a millisecond UNIX timestamp and `expiresAfterSlot` contains the last valid slot. Check these before executing if present. Not every provider appears in every response. Iterate with `Object.entries(quotes)` and pick the route with the best `outAmount`. Next steps[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/quickstart#next-steps) --------------------------------------------------------------------------------------------------- * [Stream & Execute a Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute) — full guide with transaction building, signing, and error handling * [Configure Routing](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing) — filter venues and providers, set account limits * [Authentication](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/authentication) — JWT claims reference [PreviousGet API Access](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/api-access) [NextGuides](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides) Last updated 4 months ago * [What a quote looks like](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/quickstart#what-a-quote-looks-like) * [Next steps](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/quickstart#next-steps) --- # Get API Access | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/get-api-access.md) . Free public endpoint[](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/get-api-access#free-public-endpoint) -------------------------------------------------------------------------------------------------------------------------------- A public DART endpoint is available for testing and low-volume use. No API key required. **Base URL:** `https://api.titan.exchange/dart` * **1 request per second** per IP address * REST only, JSON responses * DART provider only * * * Partner access[](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/get-api-access#partner-access) -------------------------------------------------------------------------------------------------------------------- For higher rate limits, pass your API key via header: Copy curl -X POST https://api.titan.exchange/dart/swap \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ ... }' Or using `X-API-Key`: Copy curl -X POST https://api.titan.exchange/dart/swap \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ ... }' To request a partner API key, fill out the application form: [**Apply for DART API Access**](https://tally.so/r/1AvYeL) * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/get-api-access#related-pages) ------------------------------------------------------------------------------------------------------------------ * [Overview](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/overview) — What DART is, supported pairs, and fees * [How to Use](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/how-to-use) — Endpoints, request/response format, and transaction building [PreviousOverview](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/overview) [NextHow to Use](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/how-to-use) Last updated 3 months ago * [Free public endpoint](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/get-api-access#free-public-endpoint) * [Partner access](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/get-api-access#partner-access) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/get-api-access#related-pages) --- # Limit Order Events | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/events.md) . **You can listen to limit order events by parsing through the program logs for emitted data.** Events are Borsh-serialized and emitted in program logs for every order lifecycle operation. * * * Event structure[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/events#event-structure) ----------------------------------------------------------------------------------------------------------------------- Copy #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize)] pub struct LimitOrderEvent { /// Event discriminator, used to identify the event type /// Create - 1, Take - 2, Cancel - 3, Modify - 4, Withdraw - 5, CloseExpired - 6 pub discriminator: u8, /// Instruction that triggered the event pub instruction: u8, /// The status of the order pub status: u8, /// Unique identifier for the order, used to differentiate orders for same /// (owner, input_mint, output_mint) tuple pub id: u8, /// The public key of the order pub maker: Pubkey, /// The mint of the input token pub input_mint: Pubkey, /// The mint of the output token pub output_mint: Pubkey, /// The slot at which the event was emitted pub slot: u64, /// The slot at which the order was created pub creation_slot: u64, /// The slot at which the order expires pub expiration_slot: u64, /// The amount of input tokens to be exchanged pub amount: u64, /// The amount of input tokens that have been filled pub amount_filled: u64, /// The amount of output tokens that have been exchanged pub out_amount_filled: u64, /// The amount of fees paid in the smallest unit of from_token mint pub fees_paid: u64, /// Price base in the order, in the smallest unit of output token pub price_base: u64, /// Price exponent, price is calculated as price_base * 10^(-price_exponent) pub price_exponent: u8, /// Time in order, used to determine how long the order is valid pub time_in_force: u8, /// Fee rate in ticks, used to determine the fee charged for the order pub fee_ticks: u8, } * * * Instruction discriminators[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/events#instruction-discriminators) --------------------------------------------------------------------------------------------------------------------------------------------- The `discriminator` field is always `0`. The `instruction` field identifies which operation triggered the event: * `**1**` **—** `**PlaceOrder**` — A new limit order was created. * `**2**` **—** `**TakeOrder**` — A searcher filled (partially or fully) an order. * `**3**` **—** `**CancelOrder**` — The maker cancelled the order. * `**4**` **—** `**ModifyOrder**` — The maker modified the order parameters. * `**5**` **—** `**WithdrawFilled**` — The maker withdrew filled output tokens from a partially filled order. * `**6**` **—** `**CloseExpired**` — An expired order was closed and rent reclaimed. The `status` field in the event reflects the order's state **after** the operation. Use [OrderStatus](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview#order-status) values to interpret it. * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/events#related-pages) ------------------------------------------------------------------------------------------------------------------- * [Limit Orders Overview](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview) — Order structure, time-in-force, order status, fees * [Placing Taker Orders](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order) — TakeOrder instruction and execution code * [Error Codes](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/error-codes) — Program error codes [PreviousPlacing Taker Orders](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order) [NextError Codes](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/error-codes) Last updated 4 months ago * [Event structure](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/events#event-structure) * [Instruction discriminators](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/events#instruction-discriminators) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/events#related-pages) --- # Swap V2 vs Swap V3 | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3.md) . Swap V3 is the newer version of the Titan Exchange Router. V2 is the current default; V3 is opt-in via `titanSwapVersion: 3`. **The default is changing to V3 on July 15, 2026.** Today, requests without `titanSwapVersion` use V2. After the switch, requests without `titanSwapVersion` will use V3. V2 will remain available as an explicit opt-in (`titanSwapVersion: 2`). See [Migrating to V3](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3#migrating-to-v3) below. What's new in V3[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3#whats-new-in-v3) ------------------------------------------------------------------------------------------------------------------------ **Account handling moved inside the instruction.** Output ATA creation and wSOL wrapping/unwrapping are handled inside the swap instruction itself, so a V3 route returns a single consolidated swap instruction where V2 returned several separate ones. This reduces the number of instructions you assemble into the transaction. **Separate fee payer (**`**payer**`**).** A distinct account can fund all SOL-denominated costs — network fees, ATA rent (wSOL wrap, output ATA), and rent refunds — instead of the user paying them. The payer must co-sign the transaction. Defaults to the user if not set. Enables sponsored / gasless-style swap flows. **Positive-slippage capture (**`**positiveSlippageFeeReceiver**`**).** When realized output beats the quoted amount, the surplus can be skimmed to a designated account, capped at 10 bps of the output. The receiver must be an existing token account of the output mint. Anything above the cap stays with the user. **Keep output as wSOL (**`**outputWsol**`**).** When the output mint is wrapped SOL (`So11111111111111111111111111111111111111112`), the router unwraps the result to native SOL by default. Set `outputWsol: true` to leave the output as the wSOL SPL token instead — useful when the next step in your flow expects a token account rather than native lamports. Boolean, defaults to `false`. Only has an effect when `outputMint` is wSOL. Copy { "transaction": { "userPublicKey": "72neGwRAi6QWsFQjy3PkDuYBC5GNCRwC2aUMGcrkoJuP", "outputWsol": true } } Selecting a version[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3#selecting-a-version) ------------------------------------------------------------------------------------------------------------------------------- V3 is selected per request via the `titanSwapVersion` field in `TransactionParams`. Leave it unset for V2 (the default); set it to `3` to opt into V3 and unlock `payer`, `positiveSlippageFeeReceiver`, and `outputWsol`. Copy { "transaction": { "userPublicKey": "72neGwRAi6QWsFQjy3PkDuYBC5GNCRwC2aUMGcrkoJuP", "titanSwapVersion": 3 } } > `titanSwapVersion` is the integer `3`, **not** the string `"V3"`. A string is rejected with `Failed to deserialize query string: titanSwapVersion: invalid digit found in string`. Pinning to V2[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3#pinning-to-v2) ------------------------------------------------------------------------------------------------------------------- If you aren't ready to migrate before the [default switches to V3](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3#swap-v2-vs-swap-v3) on July 15, 2026, set `titanSwapVersion: 2` explicitly. Requests that pin the version this way are unaffected by the default change and keep using V2 until it is fully retired. Pinning to V2 is a stopgap, not a long-term position. Treat the pin as a way to buy migration time — not to stay on V2 indefinitely. Migrating to V3[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3#migrating-to-v3) ----------------------------------------------------------------------------------------------------------------------- To move to V3, set `titanSwapVersion: 3` in `TransactionParams`. Once V3 becomes the default on July 15, 2026, you can drop the field entirely. V3 adds three optional fields in `TransactionParams`. None are required to migrate — adopt them only if you need what they provide: * `**payer**` — a separate account to fund the SOL-denominated costs of the swap. Must co-sign the transaction. Defaults to the user's public key if unset. * `**positiveSlippageFeeReceiver**` — the account that receives any positive-slippage surplus. * `**outputWsol**` — leave the output as wrapped SOL instead of unwrapping to native SOL, when the output mint is wSOL. Defaults to `false`. If you build the transaction yourself from the route's `instructions` and `addressLookupTables`, note that the V3 instruction set differs from V2 — a V3 route returns a single consolidated swap instruction where V2 returned several. Rebuild from the returned `instructions` rather than assuming the V2 layout. When reading quotes, set the transaction's compute-unit limit from the quote's `computeUnitsSafe` — the server's recommended value with a buffer — rather than a hard-coded number. ### Migration checklist[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3#migration-checklist) 1. Set `titanSwapVersion: 3` on your swap requests. 2. Adopt `payer`, `positiveSlippageFeeReceiver`, or `outputWsol` only if your flow needs them. 3. If you set compute-unit limits manually, use the quote's `computeUnitsSafe`. 4. After July 15, 2026 you can drop `titanSwapVersion` entirely — V3 becomes the default. Comparison[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3#comparison) ------------------------------------------------------------------------------------------------------------- Swap V2 Swap V3 Status Current default; becomes opt-in (`titanSwapVersion: 2`) on July 15, 2026 Opt-in now (`titanSwapVersion: 3`); becomes default on July 15, 2026 ATA creation + SOL wrap/unwrap Separate instructions around the swap Handled inside the swap instruction (one consolidated instruction) Separate fee payer No Yes (`payer`, must co-sign) Positive-slippage capture No Yes (≤ 10 bps, output-mint token account) Keep output as wSOL No Yes (`outputWsol`) [PreviousGuides](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides) [NextStream & Execute a Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute) Last updated 1 month ago * [What's new in V3](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3#whats-new-in-v3) * [Selecting a version](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3#selecting-a-version) * [Pinning to V2](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3#pinning-to-v2) * [Migrating to V3](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3#migrating-to-v3) * [Migration checklist](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3#migration-checklist) * [Comparison](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3#comparison) Copy { "transaction": { "userPublicKey": "72neGwRAi6QWsFQjy3PkDuYBC5GNCRwC2aUMGcrkoJuP", "titanSwapVersion": 2 } } --- # Quote Price | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price.md) . **Returns a price quote without instructions or transaction data.** Use it when you need to **display prices** without the overhead of building executable transactions. The Gateway equivalent of [GetSwapPrice](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-swap-price) . Copy GET /api/v1/quote/price **Authentication** — `Authorization: Bearer ` header or `?auth=` query param. See [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#authentication) for JWT details. * * * Query parameters[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price#query-parameters) ------------------------------------------------------------------------------------------------------------------------------------------ ### Required[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price#required) * `**inputMint**` — Input token mint address (base58). * `**outputMint**` — Output token mint address (base58). * `**amount**` — Amount in the smallest unit (e.g. lamports for SOL). **Not scaled by decimals.** ### Routing options[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price#routing-options) * `**dexes**` — Comma-separated venue labels to **include**. See [GetVenues](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#venues) for valid labels. * `**excludeDexes**` — Comma-separated venue labels to **exclude**. This endpoint **does not** accept `userPublicKey`, `feeAccount`, or any transaction-related parameters. To get executable swap data, use [Quote Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap) . * * * Response[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price#response) -------------------------------------------------------------------------------------------------------------------------- **The response body is MessagePack-encoded.** Set `Accept: application/vnd.msgpack` in your request headers. The response is a [`SwapPrice`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-swap-price) object — **the same type returned by the Titan Direct** `**GetSwapPrice**` **RPC method.** See [GetSwapPrice](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-swap-price) for the full type definition. * * * Example[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price#example) ------------------------------------------------------------------------------------------------------------------------ * * * Error responses[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price#error-responses) ---------------------------------------------------------------------------------------------------------------------------------------- * `**400**` — **Invalid parameters.** Malformed pubkey, missing required field, or value out of bounds. * `**401**` — **Missing or invalid authentication token.** Check your JWT and its claims. * `**404**` — **No routes found** for this swap pair. Try relaxing routing constraints. * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price#related-pages) ------------------------------------------------------------------------------------------------------------------------------------ * [Quote Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap) — Full quote with executable transaction instructions, ready to sign and send * [GetSwapPrice](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-swap-price) — Direct (WebSocket) equivalent; returns the same `SwapPrice` type * [Configure Routing](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing) — Venue filtering strategies using `dexes` and `excludeDexes` [PreviousQuote Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap) [NextInfo / Venues / Providers](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info) Last updated 4 months ago * [Query parameters](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price#query-parameters) * [Required](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price#required) * [Routing options](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price#routing-options) * [Response](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price#response) * [Example](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price#example) * [Error responses](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price#error-responses) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price#related-pages) Copy import { Decoder } from '@msgpack/msgpack'; // useBigInt64 required — amounts are u64 const decoder = new Decoder({ useBigInt64: true }); // 1 SOL → USDC price check const params = new URLSearchParams({ inputMint: 'So11111111111111111111111111111111111111112', // SOL outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC amount: '1000000000', // 1 SOL in lamports }); // Fetch price from Gateway const res = await fetch( `${process.env.TITAN_ENDPOINT}/api/v1/quote/price?${params}`, { headers: { 'Authorization': `Bearer ${process.env.TITAN_API_KEY}`, 'Accept': 'application/vnd.msgpack', // Required for MessagePack response }, } ); if (!res.ok) { throw new Error(`${res.status}: ${res.statusText}`); } // Decode the MessagePack response const buffer = await res.arrayBuffer(); const price = decoder.decode(new Uint8Array(buffer)) as any; console.log('Output amount:', price.amountOut); --- # StopStream | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream.md) . **Tells the server to stop sending data on an active stream.** Use it when you no longer need quotes — after executing a swap, when the user navigates away, or before opening a new stream for a different pair. Request[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream#request) --------------------------------------------------------------------------------------------------------------- Rust TypeScript Copy struct StopStreamRequest { /// ID of the stream to stop. id: u32, } Copy interface StopStreamRequest { // ID of the stream to stop. id: number; } Response[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream#response) ----------------------------------------------------------------------------------------------------------------- The server replies with `StreamStopped` containing the ID of the stopped stream. Rust TypeScript Copy struct StopStreamResponse { /// Identifier of the stream that was stopped. id: u32, } Copy interface StopStreamResponse { // Identifier of the stream that was stopped. id: number; } Behavior[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream#behavior) ----------------------------------------------------------------------------------------------------------------- * **Queued data may still arrive.** After sending `StopStream`, the server may deliver one or more `StreamData` messages that were already queued. Handle them gracefully. * `**StreamEnd**` **always follows.** The server sends a `StreamEnd` message once all queued data has been flushed. **Only after receiving** `**StreamEnd**` **is the stream fully closed.** * **Stream IDs do not survive reconnects.** If the WebSocket connection drops, all streams are implicitly ended. Old IDs are no longer valid — do not call `StopStream` for streams from a previous connection. Example[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream#example) --------------------------------------------------------------------------------------------------------------- See [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#sending-a-request) for `sendRequest` and `decodeMessage` setup. * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream#related-pages) --------------------------------------------------------------------------------------------------------------------------- * [NewSwapQuoteStream](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream) — open a streaming swap quote * [Error Handling & Reconnect](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling) — handle connection drops and stream errors * [Wire Protocol](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol) — MessagePack encoding and message envelope types [PreviousNewSwapQuoteStream](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream) [NextGetVenues / ListProviders](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers) Last updated 4 months ago * [Request](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream#request) * [Response](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream#response) * [Behavior](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream#behavior) * [Example](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream#example) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream#related-pages) Copy // Capture stream ID when a quote stream opens if ('Response' in msg && 'NewSwapQuoteStream' in msg.Response.data) { streamId = msg.Response.stream.id; } // After receiving quotes, stop the stream if ('StreamData' in msg && streamId !== undefined) { console.log('Got quotes, stopping stream', streamId); await sendRequest(ws, requestId++, { StopStream: { id: streamId } }); streamId = undefined; } // Server confirms the stream was stopped if ('Response' in msg && 'StreamStopped' in msg.Response.data) { console.log('Stream stopped:', msg.Response.data.StreamStopped.id); } // Stream is fully closed — no more data will arrive for this stream if ('StreamEnd' in msg) { console.log('Stream ended:', msg.StreamEnd.id); } --- # API Reference | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference.md) . **The precise contract for every route, object, and error code in the DCA Partner API.** Start with [Authentication](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication) for the header model, then [Endpoints](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints) for the routes. [PreviousLifecycle & Polling](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle) [NextAuthentication](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication) Last updated 1 month ago --- # API Reference | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference.md) . **Full reference for every RPC method, endpoint, type, and error code across both Titan interfaces.** Choose [Titan Direct](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway) for real-time WebSocket streams or [Titan Gateway](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway) for simple REST calls — both are backed by the same Argos routing engine. [PreviousError Handling & Reconnect](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling) [NextTitan Direct vs Titan Gateway](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway) Last updated 4 months ago --- # AI / LLM Integration | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration.md) . **Use Titan's swap infrastructure from AI agents, LLM tool-calling workflows, and autonomous trading systems.** * * * Claude Code Skill[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#claude-code-skill) -------------------------------------------------------------------------------------------------------------------------- **The fastest way to build Titan integrations with AI.** The `@titanexchange/titan-api-skill` package gives Claude Code protocol-aware code generation for the Titan API. * **Package:** [@titanexchange/titan-api-skill](https://www.npmjs.com/package/@titanexchange/titan-api-skill) * **Source:** [github.com/Titan-Pathfinder/titan-api-claude-skills](https://github.com/Titan-Pathfinder/titan-api-claude-skills) ### What it provides[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#what-it-provides) * **Protocol-aware code generation** — Generates TypeScript with correct MessagePack encoding, BigInt amounts, and bs58-decoded token mints matching the Titan WebSocket API spec. * **SDK and raw WebSocket support** — Covers both SDK-based and direct WebSocket integration depending on developer needs. * **Parameter structure enforcement** — Places fields like `slippageBps`, `intervalMs`, and `num_quotes` in their correct nested objects matching the expected request schema. * **Runnable examples included** — Ships with working TypeScript examples that can be executed directly. ### Installation[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#installation) **npx (recommended):** Copy # For current project npx @titanexchange/titan-api-skill # For all projects (global) npx @titanexchange/titan-api-skill --global # Overwrite existing install npx @titanexchange/titan-api-skill --force Then type `/titan-swap-api` in Claude Code to use the skill. **Manual install (curl):** ### Quick example[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#quick-example) Ask Claude Code: > "Help me stream USDC to SOL quotes using Titan API" Claude will generate protocol-correct code: ### Runnable examples[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#runnable-examples) The `/examples` directory contains working TypeScript examples: ### Required credentials[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#required-credentials) * `**WS_URL**` — Titan WebSocket endpoint. * `**AUTH_TOKEN**` — API authentication token. See [Get API Access](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/api-access) . * * * LLM-friendly docs (llms.txt)[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#llm-friendly-docs-llms.txt) ---------------------------------------------------------------------------------------------------------------------------------------------- **Titan's documentation is automatically available in LLM-optimized formats** via the [llms.txt](https://llmstxt.org/) standard. AI agents and LLMs can ingest the full docs without scraping HTML. * `**/llms.txt**` — Index of all doc sections with links to individual pages in Markdown format. * `**/llms-full.txt**` — The entire documentation as a single Markdown file — ideal for full-context ingestion. * **Every page as** `**.md**` — Append `.md` to any doc page URL to get the raw Markdown version. These endpoints are **generated automatically by GitBook** and stay up-to-date with every publish. No configuration needed. * * * Key things to know[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#key-things-to-know) ---------------------------------------------------------------------------------------------------------------------------- * **Protocol** — WebSocket + MessagePack (not JSON). * **Amounts** — Must be `BigInt`, not `number`. * **Token mints** — Must be `Uint8Array` via `bs58.decode()`. * **Parameters** — `slippageBps` goes in `swap`, `intervalMs` goes in `update`. * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#related-pages) ------------------------------------------------------------------------------------------------------------------ * [SDK Reference](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk) — TypeScript and Rust SDK documentation * [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection) — WebSocket setup and protocol details * [Quickstart](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/quickstart) — End-to-end integration example [PreviousCommunity & Support](https://titan-exchange.gitbook.io/titan/developer-doc/resources/community-and-support) Last updated 4 months ago * [Claude Code Skill](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#claude-code-skill) * [What it provides](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#what-it-provides) * [Installation](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#installation) * [Quick example](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#quick-example) * [Runnable examples](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#runnable-examples) * [Required credentials](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#required-credentials) * [LLM-friendly docs (llms.txt)](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#llm-friendly-docs-llms.txt) * [Key things to know](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#key-things-to-know) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration#related-pages) Copy # Global mkdir -p ~/.claude/skills/titan-swap-api curl -o ~/.claude/skills/titan-swap-api/SKILL.md \ https://raw.githubusercontent.com/Titan-Pathfinder/titan-api-claude-skills/main/SKILL.md # Project-level mkdir -p .claude/skills/titan-swap-api curl -o .claude/skills/titan-swap-api/SKILL.md \ https://raw.githubusercontent.com/Titan-Pathfinder/titan-api-claude-skills/main/SKILL.md Copy import { V1Client } from "@titanexchange/sdk-ts"; import bs58 from "bs58"; const client = await V1Client.connect(`${WS_URL}?auth=${AUTH_TOKEN}`); const { stream } = await client.newSwapQuoteStream({ swap: { inputMint: bs58.decode("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), outputMint: bs58.decode("So11111111111111111111111111111111111111112"), amount: BigInt(100_000_000), // 100 USDC — must be BigInt! }, transaction: { userPublicKey: bs58.decode(USER_PUBLIC_KEY), }, }); for await (const quotes of stream) { console.log(quotes); } Copy cd examples npm install cp .env.example .env # Edit .env with your credentials npm run stream-sdk # SDK streaming npm run stream-raw # Raw WebSocket npm run proxy # Backend proxy --- # Community & Support | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/resources/community-and-support.md) . **Connect with the Titan team and community for support, feedback, and updates.** * * * Discord[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/community-and-support#discord) --------------------------------------------------------------------------------------------------------- Join the Titan Discord for real-time help, announcements, and discussion with other developers. **Discord is the fastest way to get support.** The team actively monitors developer channels. * * * GitHub[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/community-and-support#github) ------------------------------------------------------------------------------------------------------- * **TypeScript SDK** — [github.com/Titan-Pathfinder/titan-sdk-ts](https://github.com/Titan-Pathfinder/titan-sdk-ts) * **Rust SDK crates** — [crates.io/search?q=titan-api-types](https://crates.io/search?q=titan-api-types) **Found a bug?** Open an issue on the relevant SDK repository with reproduction steps. * * * Getting API access[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/community-and-support#getting-api-access) ------------------------------------------------------------------------------------------------------------------------------- See [Get API Access](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/api-access) for instructions on obtaining your API key. [PreviousSDK Reference](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk) [NextAI / LLM Integration](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration) Last updated 2 months ago * [Discord](https://titan-exchange.gitbook.io/titan/developer-doc/resources/community-and-support#discord) * [GitHub](https://titan-exchange.gitbook.io/titan/developer-doc/resources/community-and-support#github) * [Getting API access](https://titan-exchange.gitbook.io/titan/developer-doc/resources/community-and-support#getting-api-access) --- # Connection & Negotiation | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection.md) . **Titan Direct is a persistent WebSocket connection.** All messages are binary frames encoded with [MessagePack](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol) . Before the first request, the client and server negotiate a protocol version and optional compression scheme via the `Sec-WebSocket-Protocol` header. Protocol negotiation[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#protocol-negotiation) ---------------------------------------------------------------------------------------------------------------------------------------- The client lists supported protocols in order of preference via the `Sec-WebSocket-Protocol` header. The server selects the best mutual match and confirms it in the response header. All version 1 protocols begin with `v1.api.titan.ag`, optionally suffixed with `+zstd`, `+brotli`, or `+gzip` for compression. Without a suffix, no compression is used. **Compression is applied after MessagePack encoding and must be reversed before decoding on the receiving end.** All Titan SDK examples and guides use zstd. Authentication[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#authentication) ---------------------------------------------------------------------------------------------------------------------------- **The server requires authentication before any requests can be made.** Credentials are submitted via a signed **JWT (JSON Web Token)** when opening the connection: * **Authorization header** (recommended) — `Authorization: Bearer ` * **Query parameter** — `wss://YOUR_ENDPOINT/api/v1/ws?auth=` — for clients that cannot set custom headers (e.g. browsers) ### JWT claims[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#jwt-claims) **Required:** * `**iss**` — Issuer of the JWT. * `**sub**` — Subject, a unique identifier for the authenticated user. * `**aud**` — Audience, **must be** `**api.titan.ag**`. * `**exp**` — Expiration time. Connections are refused if this time is in the past. * `**iat**` — Issued-at time. Tokens with issue times in the future are rejected. **Optional:** * `**nbf**` — Not-before time. Connections are refused if this time is in the future. * `**jti**` — JWT ID. If supported by the server, only one connection per unique `jti` value is accepted. * `**https://api.titan.ag/upk_b58**` — A Solana public key as a Base58 string. If set, this key is used for transaction generation and **any attempt to submit a different** `**userPublicKey**` **will result in an error**. Connecting[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#connecting) -------------------------------------------------------------------------------------------------------------------- Message format[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#message-format) ---------------------------------------------------------------------------------------------------------------------------- **All messages are binary WebSocket frames — text frames are ignored.** Every message is MessagePack encoded. For encoding conventions, data types, and serialization details, see [Wire Protocol](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol) . RPC interface[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#rpc-interface) -------------------------------------------------------------------------------------------------------------------------- The client interacts with the server by making requests with a given set of parameters and receiving a response (success or error) for each request. Procedure Parameters Response Stream `GetInfo` `GetInfoRequest` `ServerInfo` — `NewSwapQuoteStream` `SwapQuoteRequest` `QuoteSwapStreamResponse` `SwapQuotes` `StopStream` `StopStreamRequest` `StopStreamResponse` — `GetVenues` `GetVenuesRequest` `VenueInfo` — `ListProviders` `ListProvidersRequest` `ProviderInfo[]` — Every client request is a [`ClientRequest`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#message-envelope-types) with a numeric `id` and a `data` field containing one of the RPC methods above. The server matches responses to requests via `requestId` and responds with one of four message types: `**Response**`, `**Error**`, `**StreamData**`, or `**StreamEnd**`. See [Types Reference](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#message-envelope-types) for the full type definitions. ### Sending a request[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#sending-a-request) Use a single incrementing `requestId` counter per connection. The server returns the same `requestId` in its response, so you can correlate requests and responses without additional bookkeeping. Ping / Pong[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#ping-pong) -------------------------------------------------------------------------------------------------------------------- The client and server both support standard WebSocket Ping/Pong frames. The `ws` library handles these automatically — **no explicit handling needed** unless you want to monitor connection health manually. * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#related-pages) -------------------------------------------------------------------------------------------------------------------------- * [GetInfo](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info) — confirm the server is reachable and read default settings * [Wire Protocol](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol) — MessagePack encoding and serialization details * [Titan Direct vs Titan Gateway](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway) — choosing between WebSocket and REST [PreviousTitan Direct](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct) [NextGetInfo](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info) Last updated 4 months ago * [Protocol negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#protocol-negotiation) * [Authentication](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#authentication) * [JWT claims](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#jwt-claims) * [Connecting](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#connecting) * [Message format](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#message-format) * [RPC interface](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#rpc-interface) * [Sending a request](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#sending-a-request) * [Ping / Pong](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#ping-pong) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#related-pages) Copy import WebSocket from 'ws'; import { Encoder, Decoder } from '@msgpack/msgpack'; import { zstdCompress, zstdDecompress } from 'http-encoding'; // useBigInt64 is required — u64 values (amounts, timestamps) exceed Number.MAX_SAFE_INTEGER const encoder = new Encoder({ useBigInt64: true }); const decoder = new Decoder({ useBigInt64: true }); let useCompression = false; // Pass your API key as a query parameter const url = `${process.env.TITAN_ENDPOINT}?auth=${process.env.TITAN_API_KEY}`; // Offer zstd compression with a plaintext fallback const ws = new WebSocket(url, [\ 'v1.api.titan.ag+zstd',\ 'v1.api.titan.ag',\ ]); ws.on('open', () => { // Check which protocol the server selected useCompression = ws.protocol !== 'v1.api.titan.ag'; console.log('Connected. Negotiated protocol:', ws.protocol); }); Copy // Monotonically increasing counter — server echoes this back in responses let requestId = 0; // Encode and optionally compress before sending async function sendRequest(ws: WebSocket, id: number, data: Record) { const encoded = encoder.encode({ id, data }); ws.send(useCompression ? await zstdCompress(encoded) : encoded); } // Decompress (if needed) and decode incoming messages async function decodeMessage(raw: Buffer): Promise { const data = useCompression ? await zstdDecompress(raw) : raw; return decoder.decode(data); } // Send GetInfo to confirm the connection is live await sendRequest(ws, requestId++, { GetInfo: {} }); // Handle all four server message types ws.on('message', async (raw: Buffer) => { const msg = await decodeMessage(raw); if ('Response' in msg) { // Successful RPC response — correlate via requestId console.log('Response to request', msg.Response.requestId, msg.Response.data); } if ('Error' in msg) { // RPC error — contains requestId, code, and message console.error('Error on request', msg.Error.requestId, msg.Error.code, msg.Error.message); } if ('StreamData' in msg) { // Stream update — contains id, seq, and payload } if ('StreamEnd' in msg) { // Stream closed — check errorCode/errorMessage for abnormal termination } }); --- # GetInfo | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info.md) . `**GetInfo**` **returns the server's protocol version and the default/min/max values for all configurable settings.** Call it after connecting to confirm the server is reachable and to read the bounds you'll need for stream configuration. Request[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info#request) ------------------------------------------------------------------------------------------------------------ `GetInfoRequest` is an empty object — no parameters. The server ignores any unknown fields. Copy // Empty request — no parameters needed await sendRequest(ws, requestId++, { GetInfo: {} }); See [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#sending-a-request) for `sendRequest` and `decodeMessage` setup. Response[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info#response) -------------------------------------------------------------------------------------------------------------- The server responds with a [`ServerInfo`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) object containing the protocol version and all server settings with their defaults and bounds. Copy ws.on('message', async (raw: Buffer) => { const msg = await decodeMessage(raw); if ('Response' in msg && 'GetInfo' in msg.Response.data) { const info = msg.Response.data.GetInfo; // Protocol version — major changes are backwards-incompatible console.log('Protocol version:', info.protocolVersion); // { major: 1, minor: 2, patch: 0 } // Quote stream settings — use these bounds when configuring NewSwapQuoteStream console.log('Update interval — min/max/default (ms):', info.settings.quoteUpdate.intervalMs.min, info.settings.quoteUpdate.intervalMs.max, info.settings.quoteUpdate.intervalMs.default, ); console.log('Max quotes per update — default:', info.settings.quoteUpdate.numQuotes.default ); // How many streams you can open simultaneously on this connection console.log('Concurrent streams allowed:', info.settings.connection.concurrentStreams ); } }); ServerInfo[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info#serverinfo) ------------------------------------------------------------------------------------------------------------------ Rust TypeScript Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info#related-pages) ------------------------------------------------------------------------------------------------------------------------ * [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection) — WebSocket setup, authentication, and the `sendRequest`/`decodeMessage` helpers used above * [NewSwapQuoteStream](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream) — open a streaming swap quote using the settings from GetInfo * [Wire Protocol](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol) — MessagePack encoding and serialization details [PreviousConnection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection) [NextNewSwapQuoteStream](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream) Last updated 4 months ago * [Request](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info#request) * [Response](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info#response) * [ServerInfo](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info#serverinfo) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info#related-pages) Copy struct ServerInfo { /// Server protocol version information. protocolVersion: VersionInfo, /// Server settings and parameter bounds. settings: ServerSettings, } struct VersionInfo { /// Major version — incremented for backwards-incompatible changes. major: u16, /// Minor version — incremented for backwards-compatible changes. minor: u16, /// Patch version — informational, no data format changes. patch: u16, } struct ServerSettings { /// Settings and parameter bounds for quote updates. quoteUpdate: QuoteUpdateSettings, /// Settings and parameter bounds for swaps. swap: SwapSettings, /// Settings and parameter bounds for transaction generation. transaction: TransactionSettings, /// Settings and limits for the connection. connection: ConnectionSettings, } struct BoundedValueWithDefault { /// Minimum allowed value. min: T, /// Maximum allowed value. max: T, /// Default value when not specified in request. default: T, } struct QuoteUpdateSettings { /// Bounds and default for the `intervalMs` parameter. intervalMs: BoundedValueWithDefault, /// Bounds and default for the `numQuotes` parameter. numQuotes: BoundedValueWithDefault, } struct SwapSettings { /// Default and bounds for `slippageBps`. slippageBps: BoundedValueWithDefault, /// Default value for `onlyDirectRoutes`. onlyDirectRoutes: bool, /// Default value for `addSizeConstraint`. addSizeConstraint: bool, } struct TransactionSettings { /// Default value for `closeInputTokenAccount`. closeInputTokenAccount: bool, /// Default value for `createOutputTokenAccount`. createOutputTokenAccount: bool, } struct ConnectionSettings { /// Number of concurrent streams the user is allowed. concurrentStreams: u32, } Copy interface ServerInfo { // Server protocol version information. protocolVersion: VersionInfo; // Server settings and parameter bounds. settings: ServerSettings; } interface VersionInfo { // Major version — incremented for backwards-incompatible changes. major: number; // Minor version — incremented for backwards-compatible changes. minor: number; // Patch version — informational, no data format changes. patch: number; } interface ServerSettings { // Settings and parameter bounds for quote updates. quoteUpdate: QuoteUpdateSettings; // Settings and parameter bounds for swaps. swap: SwapSettings; // Settings and parameter bounds for transaction generation. transaction: TransactionSettings; // Settings and limits for the connection. connection: ConnectionSettings; } interface QuoteUpdateSettings { // Bounds and default for `intervalMs` parameter. intervalMs: { min: number; max: number; default: number }; // Bounds and default for `numQuotes` parameter. numQuotes: { min: number; max: number; default: number }; } interface SwapSettings { // Default and bounds for `slippageBps`. slippageBps: { min: number; max: number; default: number }; // Default value for `onlyDirectRoutes`. onlyDirectRoutes: boolean; // Default value for `addSizeConstraint`. addSizeConstraint: boolean; } interface TransactionSettings { // Default value for `closeInputTokenAccount`. closeInputTokenAccount: boolean; // Default value for `createOutputTokenAccount`. createOutputTokenAccount: boolean; } interface ConnectionSettings { // Number of concurrent streams the user is allowed. concurrentStreams: number; } --- # How to Use | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/how-to-use.md) . The DART API is a standard **JSON REST API** — no MessagePack, no WebSocket. Just HTTP requests. Free to use without an API key, or pass a key for higher rate limits (see [Get API Access](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/get-api-access) ). **Base URL:** `https://api.titan.exchange/dart` * * * `GET /health`[](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/how-to-use#get-health) ----------------------------------------------------------------------------------------------------------- Health check. Copy curl https://api.titan.exchange/dart/health Copy { "status": "ok" } * * * `GET /markets`[](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/how-to-use#get-markets) ------------------------------------------------------------------------------------------------------------- Returns the list of supported trading pairs. Copy curl https://api.titan.exchange/dart/markets Copy { "markets": [\ {\ "name": "SOL/USDC",\ "tokenA": "So11111111111111111111111111111111111111112",\ "tokenB": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"\ }\ ] } * * * `POST /swap`[](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/how-to-use#post-swap) --------------------------------------------------------------------------------------------------------- Get a swap quote with transaction-ready instructions. Compute budget instructions are pre-configured for optimal execution. **Request body (JSON):** * `**inputMint**` (string, required) — Input token mint address (base58). * `**outputMint**` (string, required) — Output token mint address (base58). * `**amount**` (string, required) — Raw amount in smallest unit (e.g. lamports). * `**userPublicKey**` (string, required) — Wallet public key (base58). Must be on-curve. * `**slippageBps**` (number, optional) — Slippage tolerance in basis points. Default: `50`. * `**computeUnitPrice**` (number, optional) — Compute unit price in microLamports. Default: `10000`. * `**includeDexes**` (string\[\], optional) — Only use these DEX venues. * `**excludeDexes**` (string\[\], optional) — Exclude these DEX venues. **Example:** **Response:** **Response fields:** * `**outputAmount**` — Expected output in smallest unit. * `**inputAmount**` — Input amount in smallest unit. * `**provider**` — Always `Titan-DART`. * `**slippageBps**` — Slippage tolerance applied. * `**instructions**` — Swap instructions with compute budget pre-configured. `programId` and `pubkey` are base58, `data` is base64. * `**addressLookupTables**` — Base58 address lookup table keys for V0 transaction compilation. **Compute budget (prepended automatically):** * `**requestHeapFrame**` — 256 KB * `**setComputeUnitLimit**` — 1,400,000 CUs * `**setComputeUnitPrice**` — configurable (default 10,000 microLamports) **Errors:** * `**400**` — Missing required fields or invalid JSON. * `**404**` — No routes found for the given pair. * `**429**` — Rate limit exceeded. * * * Building a transaction[](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/how-to-use#building-a-transaction) -------------------------------------------------------------------------------------------------------------------------------- The response includes all instructions ready to go — deserialize, build a V0 transaction, sign, and send: * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/how-to-use#related-pages) -------------------------------------------------------------------------------------------------------------- * [Overview](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/overview) — What DART is, supported pairs, and fees * [Get API Access](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/get-api-access) — Rate limits and higher-rate access [PreviousGet API Access](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/get-api-access) [NextOverview](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/overview) Last updated 3 months ago * [GET /health](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/how-to-use#get-health) * [GET /markets](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/how-to-use#get-markets) * [POST /swap](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/how-to-use#post-swap) * [Building a transaction](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/how-to-use#building-a-transaction) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/how-to-use#related-pages) Copy curl -X POST https://api.titan.exchange/dart/swap \ -H "Content-Type: application/json" \ -d '{ "inputMint": "So11111111111111111111111111111111111111112", "outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "amount": "1000000000", "userPublicKey": "YourWalletPublicKeyHere" }' Copy { "outputAmount": "84550000", "inputAmount": "1000000000", "provider": "Titan-DART", "slippageBps": 50, "instructions": [\ {\ "programId": "ComputeBudget111111111111111111111111111111",\ "accounts": [],\ "data": "AQAABAA="\ },\ {\ "programId": "ComputeBudget111111111111111111111111111111",\ "accounts": [\ {\ "pubkey": "jitodontfronttitandart111111111111111111111",\ "isSigner": false,\ "isWritable": false\ }\ ],\ "data": "AsBcFQA="\ },\ {\ "programId": "ComputeBudget111111111111111111111111111111",\ "accounts": [],\ "data": "AxAnAAAAAAAA"\ },\ {\ "programId": "T1TANpTeScyeqVzzgNViGDNrkQ6qHz9KrSBS4aNXvGT",\ "accounts": [\ {\ "pubkey": "YourWalletPublicKeyHere",\ "isSigner": true,\ "isWritable": true\ }\ ],\ "data": "..."\ }\ ], "addressLookupTables": [\ "RyXhBMnPkYJyWEkBmYAnW7A8LCKfrEgAABB2xVZrwy3"\ ] } Copy import { Connection, PublicKey, TransactionInstruction, TransactionMessage, VersionedTransaction, } from "@solana/web3.js"; // 1. Deserialize instructions from the response const instructions = response.instructions.map( (ix) => new TransactionInstruction({ programId: new PublicKey(ix.programId), keys: ix.accounts.map((acc) => ({ pubkey: new PublicKey(acc.pubkey), isSigner: acc.isSigner, isWritable: acc.isWritable, })), data: Buffer.from(ix.data, "base64"), }) ); // 2. Fetch address lookup tables const connection = new Connection("https://api.mainnet-beta.solana.com"); const altAccounts = await Promise.all( response.addressLookupTables.map(async (key) => { const alt = await connection.getAddressLookupTable(new PublicKey(key)); return alt.value; }) ); // 3. Build V0 transaction const { blockhash } = await connection.getLatestBlockhash(); const message = new TransactionMessage({ payerKey: walletPublicKey, recentBlockhash: blockhash, instructions, }).compileToV0Message(altAccounts.filter(Boolean)); const transaction = new VersionedTransaction(message); // 4. Sign and send transaction.sign([wallet]); const signature = await connection.sendTransaction(transaction); --- # Titan Direct | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct.md) . **Built for traders who can't afford stale quotes.** Titan Direct is a persistent WebSocket connection that streams live swap quotes as on-chain state changes. All messages are binary frames encoded with [MessagePack](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol) . [PreviousTitan Direct vs Titan Gateway](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway) [NextConnection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection) Last updated 4 months ago --- # Guides | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides.md) . **Practical walkthroughs for each part of a DCA integration, in the order you'll build them.** Each guide builds on the [Quickstart](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart) and assumes you've onboarded at least one user. [PreviousQuickstart](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart) [NextOnboarding (SIWS)](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding) Last updated 1 month ago --- # Titan Gateway | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway.md) . **The fastest path from zero to production-grade swap execution on Solana.** Titan Gateway exposes the same Argos routing engine as Titan Direct through simple REST endpoints. All responses are [MessagePack](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol) \-encoded. [PreviousGetSwapPrice](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-swap-price) [NextQuote Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap) Last updated 4 months ago --- # Error Codes | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/error-codes.md) . **These are the custom error codes thrown by the Titan Limit Order program.** Each maps to a specific on-chain validation failure. Copy #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum LimitOrderError { InvalidOrderStatus, // 0 InvalidAmount, // 1 InvalidMaker, // 2 InvalidMint, // 3 InvalidPrice, // 4 InvalidTokenAccountAuthority, // 5 InvalidTokenProgramId, // 6 InvalidAssociatedTokenAccountAddress, // 7 InvalidExtension, // 8 EventDeserializationError, // 9 ExpirationSlotExceeded, // 10 ExecutionTimeInForceViolation, // 11 MaxCostLimitExceeded, // 12 OrderNotExpired, // 13 } * `**0**` **—** `**InvalidOrderStatus**` — Order is not in a valid state for this operation. Check the order's [status](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview#order-status) before interacting. * `**1**` **—** `**InvalidAmount**` — Amount is zero or exceeds the remaining balance. * `**2**` **—** `**InvalidMaker**` — Maker public key does not match the order. * `**3**` **—** `**InvalidMint**` — Token mint does not match the order's input or output mint. * `**4**` **—** `**InvalidPrice**` — Price parameters are invalid (e.g. zero `price_base`). * `**5**` **—** `**InvalidTokenAccountAuthority**` — Token account authority does not match the expected owner. * `**6**` **—** `**InvalidTokenProgramId**` — Wrong token program passed for the mint. **Use SPL Token for SPL mints, SPL Token-2022 for Token-2022 mints.** * `**7**` **—** `**InvalidAssociatedTokenAccountAddress**` — ATA address does not match the expected derivation. * `**8**` **—** `**InvalidExtension**` — Token extension is not supported by the program. * `**9**` **—** `**EventDeserializationError**` — Failed to deserialize event data. * `**10**` **—** `**ExpirationSlotExceeded**` — Order has expired. **Cannot be filled after the** `**expiration_slot**`**.** * `**11**` **—** `**ExecutionTimeInForceViolation**` — Take violates the order's [time-in-force](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview#time-in-force) policy. For example, attempting a partial fill on an `AllOrNothing` order. * `**12**` **—** `**MaxCostLimitExceeded**` — Cost exceeds the taker's `max_cost_amount`. Increase the limit or reduce the take amount. * `**13**` **—** `**OrderNotExpired**` — Attempted to close an order that has not yet expired. Wait until `expiration_slot` is reached. * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/error-codes#related-pages) ------------------------------------------------------------------------------------------------------------------------ * [Limit Orders Overview](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview) — Order structure, time-in-force, order status * [Placing Taker Orders](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order) — TakeOrder instruction and execution code * [Limit Order Events](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/events) — Event structure and parsing from program logs [PreviousLimit Order Events](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/events) [NextSDK Reference](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk) Last updated 4 months ago --- # GetVenues / ListProviders | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers.md) . **Use** `**GetVenues**` **to list every on-chain venue Titan can route through, and** `**ListProviders**` **to list the quote providers that compete for best execution.** Both are lightweight lookups you can issue at any time on an open connection. * * * GetVenues[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#getvenues) ------------------------------------------------------------------------------------------------------------------------ Returns venue labels (and optionally their Solana program IDs) that are valid values for the `dexes` and `excludeDexes` routing parameters. ### Request[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#request) Rust TypeScript Copy struct GetVenuesRequest { /// Whether to include the program ID for each venue. includeProgramIds: Option, } Copy interface GetVenuesRequest { // Whether to include the program ID for each venue. includeProgramIds?: boolean; } ### Response[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#response) Rust TypeScript Copy struct VenueInfo { /// List of venue labels. Each is a valid value for `dexes` and `excludeDexes`. labels: Vec, /// Program ID for each label, same order. Only present when `includeProgramIds` is true. programIds: Option>, } Copy interface VenueInfo { // Venue labels, valid for `dexes` and `excludeDexes` parameters. labels: string[]; // Program ID for each label. Only present when `includeProgramIds` is true. programIds?: Pubkey[]; } ### Example[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#example) See [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#sending-a-request) for `sendRequest` and `decodeMessage` setup. * * * ListProviders[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#listproviders) -------------------------------------------------------------------------------------------------------------------------------- **Returns the list of quote providers currently active on the server.** Each provider independently competes to deliver the best-priced route for every quote stream. ### Request[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#request-1) Rust TypeScript ### Response[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#response-1) Rust TypeScript ### Example[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#example-1) See [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#sending-a-request) for `sendRequest` and `decodeMessage` setup. * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#related-pages) -------------------------------------------------------------------------------------------------------------------------------- * [Configure Routing](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing) — filter by venue or provider when opening a quote stream * [GetInfo](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info) — server settings and protocol version * [NewSwapQuoteStream](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream) — open a streaming swap quote [PreviousStopStream](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream) [NextGetSwapPrice](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-swap-price) Last updated 4 months ago * [GetVenues](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#getvenues) * [Request](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#request) * [Response](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#response) * [Example](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#example) * [ListProviders](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#listproviders) * [Request](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#request-1) * [Response](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#response-1) * [Example](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#example-1) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers#related-pages) Copy // Request all venues with their on-chain program IDs await sendRequest(ws, requestId++, { GetVenues: { includeProgramIds: true }, }); ws.on('message', async (raw: Buffer) => { const msg = await decodeMessage(raw); if ('Response' in msg && 'GetVenues' in msg.Response.data) { const venues = msg.Response.data.GetVenues; console.log('Venue count:', venues.labels.length); console.log('Labels:', venues.labels); // e.g. ['Raydium', 'Whirlpool', 'Phoenix', 'Meteora', ...] } }); Copy struct ListProvidersRequest { /// Whether to include icon URLs for each provider. /// By default, icons are not included. includeIcons: Option, } Copy interface ListProvidersRequest { // Whether to include icon URLs for each provider. // By default, icons are not included. includeIcons?: boolean; } Copy struct ProviderInfo { /// Provider identifier. Valid value for the `providers` routing parameter. id: String, /// Human-readable display name. name: String, /// What kind of provider this is. kind: ProviderKind, /// URI for a 48x48 icon, if requested and available. iconUri48: Option, } enum ProviderKind { DexAggregator, RFQ, } Copy interface ProviderInfo { // Provider identifier. Valid for the `providers` routing parameter. id: string; // Human-readable display name. name: string; // What kind of provider this is. kind: ProviderKind; // URI for a 48x48 icon, if requested and available. iconUri48?: string; } type ProviderKind = "DexAggregator" | "RFQ"; Copy // Request all active providers await sendRequest(ws, requestId++, { ListProviders: { includeIcons: false }, }); ws.on('message', async (raw: Buffer) => { const msg = await decodeMessage(raw); if ('Response' in msg && 'ListProviders' in msg.Response.data) { const providers = msg.Response.data.ListProviders; providers.forEach((p: any) => { console.log(`${p.name} (${p.id}) — ${p.kind}`); // e.g. "Titan (Titan) — DexAggregator" }); } }); --- # Lifecycle & Polling | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle.md) . An order moves through a fixed set of statuses from creation to a terminal state. Titan doesn't push these changes — there are no webhooks today — so you poll the read endpoints while a user is looking at their DCA surfaces. Order statuses[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle#order-statuses) ------------------------------------------------------------------------------------------------------------------------ Copy intent ──▶ pending ──confirm──▶ active ──cycle..N──▶ completed │ ├─ pause/resume ↔ paused │ ├─ executing ── (during a cycle) │ ├─ pending_modification ── (during modify signing) │ ├─ failed ── (retry budget exhausted) ── unspent input auto-returned │ (retry ──▶ active only in the brief pre-return window) │ ├─ cancelled ── (user cancel) │ └─ expired ── (config.expiresAt reached) The full enum on every order: `pending | active | executing | pending_modification | paused | completed | cancelled | failed | expired`. `executing` and `pending_modification` are transient states an order passes through during a cycle or a modify-signing window. The order's `previousStatus` holds the resting state it'll return to, so you can keep rendering "active" or "paused" instead of flickering. Automatic input return[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle#automatic-input-return) ---------------------------------------------------------------------------------------------------------------------------------------- When an order permanently fails, Titan returns its remaining unspent input to the user's external wallet on its own — no partner action, no user signature. Funds reserved for the user's other active orders are untouched. This is why a failed order's `withdrawalStatus` transitions to `completed` without you doing anything. Don't build a manual "withdraw failed order" step — surface the return via `withdrawalStatus` and `withdrawalTxHash` instead. `withdrawalStatus` tracks the return of an order's funds: `none → pending → completed` (or back to `none` via abandon). It advances automatically for failed orders, and on demand when you call order-level withdraw for a `completed` or `cancelled` order. `withdrawalTxHash` holds the on-chain signature of the return — a real signature, or `null` when no transfer was needed. What to poll[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle#what-to-poll) -------------------------------------------------------------------------------------------------------------------- Surface Endpoint Active-orders list (user's DCA dashboard) `GET /me/orders/active` Single-order detail (user has it open) `GET /dca/{orderId}` Execution history (during/after a cycle) `GET /orders/{orderId}/executions` Back-office reconciliation `GET /partners/me/executions?createdAtGte=…` A few rules that keep polling cheap and correct: **Don't poll during the two-step flows.** Between `intent` → user signs → `confirm`, just wait for the signature and call `confirm`. Pending-order transitions are deterministic once `confirm` returns. **Back off on** `**EXECUTION_IN_FLIGHT**`**.** A `409` from cancel or withdraw means a swap is still being reconciled. Retry every few seconds; the server-side recovery loop settles it within a small bounded window. **Don't double-poll the same user.** If your UI has both a list and a detail view open, share state in your frontend rather than polling both endpoints for the same data. Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle#related-pages) ---------------------------------------------------------------------------------------------------------------------- * [Create & Manage Orders](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders) — the state transitions you trigger * [Withdrawals](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/withdrawals) — how `withdrawalStatus` advances * [Order & Execution Schema](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema) — every status-related field [PreviousPlatform Fees](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/platform-fees) [NextAPI Reference](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference) Last updated 1 month ago * [Order statuses](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle#order-statuses) * [Automatic input return](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle#automatic-input-return) * [What to poll](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle#what-to-poll) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle#related-pages) --- # Overview | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/overview.md) . > **Beta** — The DART API is currently in beta. The DART API provides swap quotes for selected token pairs on Solana via Titan's **DART (Dynamically Allocated Real Time)** engine. This is a dedicated DART-only endpoint — it exclusively uses the Titan DART provider for routing. DART dynamically re-optimizes trades at the exact moment of execution, not just at quote time — **guaranteeing best execution when it matters most.** **Base URL:** `https://api.titan.exchange/dart` **Free to use** · No API key required · JSON responses · Up to 1 bps fee · 1 req/sec rate limit * * * Supported pairs[](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/overview#supported-pairs) ---------------------------------------------------------------------------------------------------------------- Use `GET /markets` to discover pairs programmatically, or reference the list below. * SOL/USDC * SOL/USDT * USDT/USDC * cbBTC/USDC * wETH/USDC * TRUMP/USDC * ZEC/USDC * USD1/USDC * HYPE/USDC * PUMP/USDC * PENGU/USDC * FARTCOIN/USDC * syrupUSD/USDC * PYUSD/USDC * USDG/USDC * CASH/USDC * AAVE/USDC * MEGA/USDC * SPCX/USDC * MU/USDC The `/swap` endpoint works on supported token pairs listed above. * * * Rate limits & fees[](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/overview#rate-limits-and-fees) ------------------------------------------------------------------------------------------------------------------------ * **1 request per second** per IP address. HTTP 429 if exceeded. * **Up to 1 bps fee** per swap, handled by the on-chain program. * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/overview#related-pages) ------------------------------------------------------------------------------------------------------------ * [Get API Access](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/get-api-access) — Free vs partner access, API keys * [How to Use](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/how-to-use) — Endpoints, request/response format, and transaction building [PreviousError Codes](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes) [NextGet API Access](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/get-api-access) Last updated 1 month ago * [Supported pairs](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/overview#supported-pairs) * [Rate limits & fees](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/overview#rate-limits-and-fees) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/overview#related-pages) --- # Titan Direct vs Titan Gateway | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway.md) . Two interfaces to the same execution infrastructure. Both route through Titan's advanced algorithms, return the same quote types, and produce identical quote quality and transaction output. **The difference is interface and workflow, not routing quality.** Titan Direct — WebSocket API[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway#titan-direct-websocket-api) ------------------------------------------------------------------------------------------------------------------------------------------------------ Titan Direct is a WebSocket API built for users where latency and execution are critical to their operations. Market makers, prop trading desks, and quant/algo traders operate in an environment where quote freshness and fill latency are existential. A persistent WebSocket connection eliminates the overhead of repeated HTTP handshakes and allows Titan to push live quote updates without polling. Routes are served by Argos, Titan's proprietary routing engine — giving users the best available price across the full liquidity landscape. **Titan Direct is the only WebSocket-native trading API on Solana. Built for traders who can't afford stale quotes.** **Use Direct when you need:** * Real-time streaming quotes that refresh automatically as on-chain state changes * Persistent connections for trading bots, market making, or algorithmic strategies * The lowest possible latency between quote and execution Titan Gateway — REST API[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway#titan-gateway-rest-api) ---------------------------------------------------------------------------------------------------------------------------------------------- Titan Gateway is a REST API built for developers and projects integrating swap functionality into products — wallets, aggregators, DeFi protocols, and consumer apps. These teams work in REST environments and do not need to redesign their architecture for a WebSocket connection. **Titan Gateway is the fastest path from zero to production-grade swap execution on Solana.** **Gateway is not a simplified version of Direct.** It is purpose-built for integration workflows, backed by the same Argos routing engine that powers Direct — **the routing quality is identical.** The differentiation is purely about interface and integration workflow. **Use Gateway when you need:** * Standard REST endpoints that fit into existing backend infrastructure * A single quote per user action — one-click swap buttons, price displays, or backend services * Drop-in integration without WebSocket infrastructure Comparison[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway#comparison) -------------------------------------------------------------------------------------------------------------------- Titan Direct Titan Gateway Interface WebSocket REST Quote delivery Streaming — continuous updates Per-request — one response per call Connection Persistent Stateless API endpoint mapping[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway#api-endpoint-mapping) ---------------------------------------------------------------------------------------------------------------------------------------- Every Titan Direct RPC method has a corresponding Gateway REST endpoint. The request parameters and response types are the same. Titan Gateway Titan Direct `GET /api/v1/info` `GetInfo` `GET /api/v1/providers` `ListProviders` `GET /api/v1/venues` `GetVenues` `GET /api/v1/quote/swap` `NewSwapQuoteStream` Which should I use?[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway#which-should-i-use) ------------------------------------------------------------------------------------------------------------------------------------- If you're not sure, start with Titan Direct. The [`@titanexchange/sdk-ts`](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk) SDK handles the WebSocket connection, MessagePack encoding, and compression negotiation for you. If your architecture requires REST or a persistent connection isn't practical, use Titan Gateway — you get the same routing quality through a familiar request/response pattern. Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway#related-pages) -------------------------------------------------------------------------------------------------------------------------- * [Quickstart](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/quickstart) — first quote with both Direct and Gateway * [Stream & Execute a Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute) — full Titan Direct guide * [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection) — WebSocket protocol details * [Wire Protocol](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol) — MessagePack encoding and compression [PreviousAPI Reference](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference) [NextTitan Direct](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct) Last updated 4 months ago * [Titan Direct — WebSocket API](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway#titan-direct-websocket-api) * [Titan Gateway — REST API](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway#titan-gateway-rest-api) * [Comparison](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway#comparison) * [API endpoint mapping](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway#api-endpoint-mapping) * [Which should I use?](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway#which-should-i-use) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway#related-pages) --- # Info / Venues / Providers | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info.md) . **Three lightweight endpoints for reading server configuration and discovering available venues and providers.** All are simple GET requests you can issue at any time. **All responses are MessagePack-encoded.** Set `Accept: application/vnd.msgpack` in your request headers. **Authentication** — `Authorization: Bearer ` header or `?auth=` query param. See [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#authentication) for JWT details. * * * Info[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#info) ----------------------------------------------------------------------------------------------------------- Copy GET /api/v1/info **Returns server settings, protocol version, and configurable parameter bounds.** The REST equivalent of [GetInfo](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info) . Call it to read the defaults and limits you'll need when configuring swap requests. For full `ServerInfo` type definitions (including `VersionInfo`, `ServerSettings`, `BoundedValueWithDefault`, and all sub-types), see the [GetInfo](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info) reference page. ### Example[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#example) Copy import { Decoder } from '@msgpack/msgpack'; // useBigInt64 required — 64-bit integer fields (amounts, timestamps) const decoder = new Decoder({ useBigInt64: true }); // Fetch server info from the Gateway REST endpoint const res = await fetch( `${process.env.TITAN_ENDPOINT}/api/v1/info`, { headers: { 'Authorization': `Bearer ${process.env.TITAN_API_KEY}`, 'Accept': 'application/vnd.msgpack', }, } ); // Decode the MessagePack response const info = decoder.decode(new Uint8Array(await res.arrayBuffer())) as any; // Protocol version — major changes are backwards-incompatible console.log('Protocol version:', info.protocolVersion); // Quote stream settings — use these bounds when configuring requests console.log('Default update interval:', info.settings.quoteUpdate.intervalMs.default, 'ms'); console.log('Concurrent streams allowed:', info.settings.connection.concurrentStreams); * * * Venues[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#venues) --------------------------------------------------------------------------------------------------------------- **Returns the list of on-chain venues available for routing.** Each label is a valid value for the `dexes` and `excludeDexes` query parameters on [Quote Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap) and [Quote Price](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price) . For full `VenueInfo` type definitions, see the [GetVenues / ListProviders](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers) reference page. ### Query parameters[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#query-parameters) * `**includeProgramIds**` — `"true"` to include the Solana program ID for each venue. ### Example[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#example-1) * * * Providers[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#providers) --------------------------------------------------------------------------------------------------------------------- **Returns the list of active quote providers.** Each `id` is a valid value for the `providers` query parameter on [Quote Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap) . Each provider independently competes to deliver the best-priced route. For full `ProviderInfo` and `ProviderKind` type definitions, see the [GetVenues / ListProviders](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers) reference page. ### Query parameters[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#query-parameters-1) * `**includeIcons**` — `"true"` to include 48×48 icon URIs for each provider. ### Example[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#example-2) * * * Error responses[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#error-responses) --------------------------------------------------------------------------------------------------------------------------------- * `**400**` — **Invalid parameters.** Malformed query parameter value. * `**401**` — **Missing or invalid authentication token.** Check your JWT and its claims. * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#related-pages) ----------------------------------------------------------------------------------------------------------------------------- * [GetInfo](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info) — Direct (WebSocket) equivalent with full `ServerInfo` type definitions * [GetVenues / ListProviders](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers) — Direct (WebSocket) equivalents with full `VenueInfo` and `ProviderInfo` type definitions * [Configure Routing](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing) — Use venue and provider IDs to filter and customize quote routing * [Titan Direct vs Titan Gateway](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway) — Choosing between WebSocket and REST [PreviousQuote Price](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price) [NextWire Protocol](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol) Last updated 4 months ago * [Info](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#info) * [Example](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#example) * [Venues](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#venues) * [Query parameters](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#query-parameters) * [Example](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#example-1) * [Providers](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#providers) * [Query parameters](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#query-parameters-1) * [Example](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#example-2) * [Error responses](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#error-responses) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#related-pages) Copy GET /api/v1/venues Copy // Fetch all available venues with their on-chain program IDs const res = await fetch( `${process.env.TITAN_ENDPOINT}/api/v1/venues?includeProgramIds=true`, { headers: { 'Authorization': `Bearer ${process.env.TITAN_API_KEY}`, 'Accept': 'application/vnd.msgpack', }, } ); // Decode and inspect the venue list const venues = decoder.decode(new Uint8Array(await res.arrayBuffer())) as any; console.log('Available venues:', venues.labels); // e.g. ['Raydium', 'Whirlpool', 'Phoenix', 'Meteora', ...] Copy GET /api/v1/providers Copy // Fetch all active quote providers const res = await fetch( `${process.env.TITAN_ENDPOINT}/api/v1/providers`, { headers: { 'Authorization': `Bearer ${process.env.TITAN_API_KEY}`, 'Accept': 'application/vnd.msgpack', }, } ); // Decode and iterate over the provider list const providers = decoder.decode(new Uint8Array(await res.arrayBuffer())) as any[]; for (const p of providers) { console.log(`${p.name} (${p.id}) — ${p.kind}`); // e.g. "Titan (Titan) — DexAggregator" } --- # GetSwapPrice | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-swap-price.md) . **Returns a price quote without instructions or transaction data.** Use it when you need to display prices in a UI without the overhead of building executable transactions — lighter weight than `NewSwapQuoteStream`. This is a one-shot request, not a stream. The server finds the best direct route and uses the simulated output to determine the price. Request[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-swap-price#request) ------------------------------------------------------------------------------------------------------------------ Rust TypeScript Copy struct SwapPriceRequest { /// Address of the input mint of the swap. inputMint: Pubkey, /// Address of the desired output token for the swap. outputMint: Pubkey, /// Raw number of tokens to swap, not scaled by decimals. amount: u64, /// If set, constrain quotes to the given set of DEXes. dexes: Option>, /// If set, exclude the following DEXes when determining routes. excludeDexes: Option>, } Copy interface SwapPriceRequest { // Address of the input mint. inputMint: Pubkey; // Address of the output mint. outputMint: Pubkey; // Raw number of tokens to swap. Use BigInt. amount: number | bigint; // If set, constrain to these DEXes. dexes?: string[]; // If set, exclude these DEXes. excludeDexes?: string[]; } Response[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-swap-price#response) -------------------------------------------------------------------------------------------------------------------- Rust TypeScript Copy struct SwapPrice { /// Identifier for this price quote. id: String, /// Address of the input mint. inputMint: Pubkey, /// Address of the output mint. outputMint: Pubkey, /// Amount that was used for the price. amountIn: u64, /// The amount out of the best simulated quote. amountOut: u64, } Copy interface SwapPrice { // Identifier for this price quote. id: string; // Address of the input mint. inputMint: Pubkey; // Address of the output mint. outputMint: Pubkey; // Amount used for pricing. amountIn: number | bigint; // Best simulated output amount. amountOut: number | bigint; } **The response does not include** `**instructions**`**,** `**addressLookupTables**`**, or any transaction data.** To get executable swap data, use [NewSwapQuoteStream](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream) or the Gateway [Quote Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap) endpoint. Example[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-swap-price#example) ------------------------------------------------------------------------------------------------------------------ See [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#sending-a-request) for `sendRequest` and `decodeMessage` setup. * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-swap-price#related-pages) ------------------------------------------------------------------------------------------------------------------------------ * [NewSwapQuoteStream](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream) — full quote with executable instructions * [Gateway Quote Price](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price) — REST equivalent * [Configure Routing](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing) — venue filtering with `dexes` and `excludeDexes` [PreviousGetVenues / ListProviders](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers) [NextTitan Gateway](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway) Last updated 1 month ago * [Request](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-swap-price#request) * [Response](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-swap-price#response) * [Example](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-swap-price#example) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-swap-price#related-pages) Copy import WebSocket from 'ws'; // highlight-next-line import { Encoder, Decoder } from '@msgpack/msgpack'; import { compressSync, decompressSync } from 'fflate'; // zstd-compatible deflate import bs58 from 'bs58'; // --- Encoder / Decoder with BigInt support for u64 fields --- // highlight-start const encoder = new Encoder({ useBigInt64: true }); const decoder = new Decoder({ useBigInt64: true }); // highlight-end // Connect with zstd sub-protocol for compressed frames const url = `${process.env.TITAN_ENDPOINT}?auth=${process.env.TITAN_API_KEY}`; // highlight-next-line const ws = new WebSocket(url, ['v1.api.titan.ag+zstd', 'v1.api.titan.ag']); let requestId = 0; // Pubkeys as 32-byte binary — MessagePack sends these as raw bytes const SOL = bs58.decode('So11111111111111111111111111111111111111112'); const USDC = bs58.decode('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); /** Encode, optionally compress, and send a request frame. */ function sendRequest(ws: WebSocket, id: number, data: Record) { // highlight-next-line const payload = encoder.encode({ id, data }); // If the server negotiated the +zstd sub-protocol, compress before sending const frame = ws.protocol === 'v1.api.titan.ag+zstd' ? compressSync(new Uint8Array(payload)) : payload; ws.send(frame); } /** Decompress (if needed) and decode an incoming frame. */ function decodeMessage(raw: Buffer): any { const bytes = ws.protocol === 'v1.api.titan.ag+zstd' ? decompressSync(new Uint8Array(raw)) : raw; // highlight-next-line return decoder.decode(bytes); } ws.on('open', () => { // Request a price-only quote — no transaction data returned sendRequest(ws, requestId++, { GetSwapPrice: { inputMint: SOL, outputMint: USDC, amount: 1_000_000_000n, // 1 SOL in lamports }, }); }); ws.on('message', async (raw: Buffer) => { const msg = decodeMessage(raw); // highlight-start // Price response — use amountOut for display if ('Response' in msg && 'GetSwapPrice' in msg.Response.data) { const price = msg.Response.data.GetSwapPrice; console.log('Output amount:', price.amountOut); // BigInt ws.close(); } // highlight-end if ('Error' in msg) { console.error(`Error ${msg.Error.code}: ${msg.Error.message}`); ws.close(); } }); --- # Platform Fees | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/platform-fees.md) . Titan DCA can take a platform fee on each cycle's swap output and send it to a wallet you control. Fees run through Titan's native swap fee mechanism, so they're collected at execution time, not billed separately. How it's configured[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/platform-fees#how-its-configured) ------------------------------------------------------------------------------------------------------------------------------------- The defaults and ceilings live on your tenant, set during onboarding: Setting Where it lives Notes Fee collection wallet Tenant config (set at onboarding) The Solana address that **receives** your fees. Provide one to collect fees; omit it to run fee-free. `platformFeeBps` Tenant config Your default fee in basis points (`10` = 0.1%). `maxFeeBps` Tenant config Hard ceiling on per-order overrides. `platformFee.bps` Per-order, on `POST /orders/intent` Overrides the default for one order. Must be `0 ≤ bps ≤ maxFeeBps`. Omit to use the tenant default. With no fee wallet configured, fees are **disabled** for your tenant — nothing is taken regardless of `bps`. The wallet is set once at onboarding and isn't changeable via the API; changing it later is an operational request to Titan. The fee wallet must accept arbitrary SPL tokens, because the fee mint varies per cycle (see below) and this wallet accumulates whatever each cycle produces across multiple mints. Use a standard self-custodial Solana wallet, not a single-token deposit address. Which mint the fee is taken in[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/platform-fees#which-mint-the-fee-is-taken-in) ------------------------------------------------------------------------------------------------------------------------------------------------------------ Selection is deterministic, checked in order: 1. If the **output** mint is a liquid mint (USDC, USDT, or WSOL), the fee is taken in the output mint. 2. Otherwise, if the **input** mint is liquid, the fee is taken in the input mint. 3. Otherwise, the fee is taken in the output mint. So a `USDC → USDT` cycle takes the fee in USDT (output wins). A `BONK → USDC` cycle takes it in USDC (output is liquid). A `BONK → WIF` cycle falls through to rule 3 and takes it in WIF. Per-order override[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/platform-fees#per-order-override) ------------------------------------------------------------------------------------------------------------------------------------ Pass `platformFee.bps` on `POST /orders/intent` to override your tenant default for a single order — including `0` to waive the fee on that order, subject to your contract: A `bps` above your `maxFeeBps` returns `400 VALIDATION_ERROR` with `details.maxAllowed` echoing the ceiling. Reconciling what was charged[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/platform-fees#reconciling-what-was-charged) -------------------------------------------------------------------------------------------------------------------------------------------------------- Every execution stores a fee snapshot. Read it per order via `GET /orders/{orderId}/executions`, or across your whole tenant for billing via `GET /partners/me/executions`: All four `platformFee*` fields populate together when a fee was charged. They're all `null` when no fee was taken — either the order's effective `bps` was `0`, or your tenant has no fee wallet configured. Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/platform-fees#related-pages) -------------------------------------------------------------------------------------------------------------------------- * [Create & Manage Orders](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders) — where `platformFee.bps` is set * [Order & Execution Schema](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema) — the full execution row, including the fee snapshot * [Endpoints → Partner reporting](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#partner-reporting) — tenant-wide execution rows for billing [PreviousWithdrawals](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/withdrawals) [NextLifecycle & Polling](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle) Last updated 1 month ago * [How it's configured](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/platform-fees#how-its-configured) * [Which mint the fee is taken in](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/platform-fees#which-mint-the-fee-is-taken-in) * [Per-order override](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/platform-fees#per-order-override) * [Reconciling what was charged](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/platform-fees#reconciling-what-was-charged) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/platform-fees#related-pages) Copy await callTitanDca('/orders/intent', { method: 'POST', sub, body: { orderType: 'dca', userPubkey, platformFee: { bps: 25 }, // 0.25% on this order; must be ≤ maxFeeBps config: { /* … */ }, }, }); Copy { "platformFeeWallet": "FeEa…", "platformFeeBps": 50, "platformFeeMint": "EPjFW…", "platformFeeAmount": "25000" } --- # Overview | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview.md) . **Titan's Limit Orders are designed to thrive in a competitive environment where searchers play a central role.** By participating as a searcher, you gain access to a marketplace of on-chain limit orders. Limit orders are resting on-chain and can be partially or completely filled. **Fees are charged to takers** and are charged as `output_mint` tokens. **Program address:** `TitanLozLMhczcwrioEguG2aAmiATAPXdYpBg3DbeKK` * * * Order structure[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview#order-structure) ------------------------------------------------------------------------------------------------------------------------- Each limit order is a PDA derived from the maker's public key, input mint, output mint, and an order ID. Copy use pinocchio::pubkey::{create_program_address, Pubkey}; use bytemuck::{Pod, Zeroable}; /// Limit order structure #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] pub struct LimitOrder { // The public key of the order, pub maker: Pubkey, // Input mint of the limit order pub input_mint: Pubkey, // Output mint of the limit order pub output_mint: Pubkey, // Slot which the order was created pub creation_slot: u64, // The slot at which the order expires pub expiration_slot: u64, // The amount of input tokens to be exchanged pub amount: u64, // The amount of input tokens that have been filled pub amount_filled: u64, // The amount of output tokens that have been exchanged. pub out_amount_filled: u64, // The amount of output tokens that the maker has withdrawn. pub out_amount_withdrawn: u64, // The amount of fees paid in the smallest unit of from_token mint. pub fees_paid: u64, // Price base in the order, in the smallest unit of output token pub price_base: u64, // Price exponent, price is calculated as price_base * 10^(-price_exponent) pub price_exponent: u8, // The status of the order pub status: u8, // Bump seed for the limit order PDA pub bump: u8, // Unique identifier for the order, used to differentiate orders for same // (owner, input_mint, output_mint) tuple pub id: u8, // Bump seed for the input mint vault PDA pub input_mint_vault_bump: u8, // Bump seed for the output mint vault PDA pub output_mint_vault_bump: u8, // Time in order pub time_in_force: u8, // Fees ticks rate for the order from takers. pub fee_ticks: u8, } **PDA seeds:** `["order", maker, input_mint, output_mint, id, bump]` **Account size:** 168 bytes. ### PDA derivation[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview#pda-derivation) * * * Price calculation[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview#price-calculation) ----------------------------------------------------------------------------------------------------------------------------- Price is stored as `price_base * 10^(-price_exponent)`. For example, a 100 USDC → 1 SOL order uses `price_base = 1` and `price_exponent = 2`, giving a price of `0.01` output tokens per input token. * * * Fees[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview#fees) --------------------------------------------------------------------------------------------------- Fees are charged to takers in the **output token**. The fee rate is stored as `fee_ticks` on the order. **The minimum fee is always 1 unit of the output token.** **Fee receiver address:** `Bq5ZzfiU3vTiJPrBJFcr98BnUy9Wc1dg9ASeycB2tX1C` * * * Time-in-force[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview#time-in-force) --------------------------------------------------------------------------------------------------------------------- Each order carries a time-in-force policy that controls fill behavior. * `**GoodTillCancelled**` **(0)** — Remains open until fully filled or cancelled. **Partial fills allowed.** * `**TakeCancelsOrder**` **(1)** — Closes after any take, regardless of fill amount. * `**AllOrNothing**` **(2)** — Takes must completely fill the remaining amount. * `**ImmediateOrCancel**` **(3)** — Same as `TakeCancelsOrder`, but **must be filled in the same slot as creation.** * `**FillOrKill**` **(4)** — Same as `AllOrNothing`, but **must be filled in the same slot as creation.** * * * Order status[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview#order-status) ------------------------------------------------------------------------------------------------------------------- * `**Open**` **(0)** — Can be partially filled, fully filled, or cancelled. * `**PartiallyFilled**` **(1)** — Some amount filled. Can still be filled or cancelled. * `**Filled**` **(2)** — Fully filled. **Terminal state.** * `**Cancelled**` **(3)** — Cancelled by maker. **Terminal state.** * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview#related-pages) --------------------------------------------------------------------------------------------------------------------- * [Placing Taker Orders](https://github.com/Titan-Pathfinder/titan-gitbook-doc/blob/main/limit-orders/take-order.md) — TakeOrder instruction, accounts, WSOL handling, and full execution code * [Limit Order Events](https://github.com/Titan-Pathfinder/titan-gitbook-doc/blob/main/limit-orders/events.md) — Event structure and parsing from program logs * [Error Codes](https://github.com/Titan-Pathfinder/titan-gitbook-doc/blob/main/limit-orders/error-codes.md) — Program error codes [PreviousLimits & Idempotency](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits) [NextPlacing Taker Orders](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order) Last updated 4 months ago * [Order structure](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview#order-structure) * [PDA derivation](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview#pda-derivation) * [Price calculation](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview#price-calculation) * [Fees](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview#fees) * [Time-in-force](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview#time-in-force) * [Order status](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview#order-status) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview#related-pages) Copy impl LimitOrder { pub const SEEDS: &'static [u8] = b"order"; pub const LEN: usize = 168; /// Get the pda address for the limit order, given the maker, input mint, /// output mint, id and bump. pub fn get_pda_address( maker: &Pubkey, input_mint: &Pubkey, output_mint: &Pubkey, id: u8, bump: u8, ) -> Result { let b0 = &[id]; let b1 = &[bump]; let seeds_with_bump = [\ LimitOrder::SEEDS,\ maker.as_ref(),\ input_mint.as_ref(),\ output_mint.as_ref(),\ b0,\ b1,\ ] .to_vec(); create_program_address(&seeds_with_bump, &crate::ID) } } Copy impl LimitOrder { /// Calculate the costs and fees for a given amount and fee ticks. pub fn calculate_costs_and_fee( &self, amount: u64, fee_ticks: u8, ) -> Result<(u64, u64), ProgramError> { let amount_u128 = amount as u128; let price_base = self.price_base as u128; let price_exponent = 10u128.pow(self.price_exponent as u32); let fee_units_u128 = (fee_ticks as u16).saturating_mul(FEE_TICK_UNITS as u16) as u128; // Calculate the transfer amount and fee amount. // Should never overflow, since its u64 * u64 // Use method to perform ceiling math division: (a + b - 1) / b let cost_u128 = amount_u128 .saturating_mul(price_base) .checked_add(price_exponent.saturating_sub(1)) .ok_or(ProgramError::ArithmeticOverflow)? .saturating_div(price_exponent); let cost: u64 = cost_u128 .try_into() .map_err(|_| ProgramError::ArithmeticOverflow)?; let fees: u64 = cost_u128 .saturating_mul(fee_units_u128) .saturating_div(FEE_TICK_DIVISOR) .try_into() .map_err(|_| ProgramError::ArithmeticOverflow)?; Ok((cost, fees.max(1))) } /// Amount left to be filled in the order. pub fn get_remaining_amount(&self) -> u64 { self.amount.saturating_sub(self.amount_filled) } } Copy fee_units = fee_ticks × 25 fee = cost × fee_units / 1,000,000 Copy /// Fee tick units pub const FEE_TICK_UNITS: u8 = 25; /// 1e6 units = 0.0001, used to convert fee tick rate to fee basis points pub const FEE_TICK_DIVISOR: u128 = 1_000_000; Copy /// Time in Force (TIF) for limit orders. Provides different behaviors for /// how long an order remains active and how it can be filled. #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq)] pub enum TimeInForce { /// Order is good until cancelled. Partial takes are allowed. GoodTillCancelled = 0, /// After taking any amount, order is closed. TakeCancelsOrder = 1, /// Takes must completely fill the order. AllOrNothing = 2, /// Same as TakeCancelsOrder but it must be filled at the same time of creation. ImmediateOrCancel = 3, /// Same as AllOrNothing but it must be filled at the same time of creation. FillOrKill = 4, } Copy /// Order status for limit orders. Indicates the current state of the order /// and how it can be interacted with. #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq)] pub enum OrderStatus { /// Order is open, can be partially filled, filled, cancelled Open = 0, /// Order is partially filled, can be filled or cancelled PartiallyFilled = 1, /// Order is filled, terminates, used for event logging Filled = 2, /// Order is cancelled, terminates, used for event logging Cancelled = 3, } --- # Placing Taker Orders | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order.md) . **Searchers fill orders by invoking the** `**TakeOrder**` **instruction (discriminator** `**2**`**).** Orders can be partially or fully fulfilled — in both cases the taker receives their tokens immediately. * * * Fill behavior[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#fill-behavior) ----------------------------------------------------------------------------------------------------------------------- ### Full fill[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#full-fill) When an order is fully filled, the program automatically: 1. **Creates the maker's ATA** for the output tokens (if needed). 2. **Refunds the original rent** used in the Limit Order back to the maker. 3. **Reimburses any lamports** back to the taker if they had to pay for any ATA creation. 4. **For WSOL output** — returns funds back to the maker as SOL instead of WSOL. ### Partial fill[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#partial-fill) For partial fills, the taker receives their tokens immediately. The remaining input tokens stay in the program vault. **The maker can withdraw filled output tokens at any time.** ### Example: 100 USDC → 1 SOL with 5 BPS fee[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#example-100-usdc-1-sol-with-5-bps-fee) 1. User creates a limit order paying the rent and depositing 100 USDC into the vault. Price is set to `0.01`. 2. Adjusting for fees — when it is favourable to trade 1.0005 SOL → 100 USDC, the taker comes in and takes the full order. 3. Under the hood, taker deposits 1.0005 SOL into the vault. 100 USDC is moved from the vault to the taker's ATA, 0.0005 WSOL fee is moved to the fee receiver's wallet. 4. Contract determines the limit order is fulfilled. Since the output is SOL, the special WSOL edge case is handled: * The contract expects a **taker-owned WSOL (non-ATA) token account** is passed into the call. * The WSOL vault sends 1 SOL to this token account and **closes it out to the maker**, crediting their wallet balance with the 1 SOL. * The limit order is closed and rent is sent to the taker. * The taker sends the rent funds to the maker subtracting any rent they paid for the WSOL token account. **For partial fills**, the above example holds — just skip step 4. **For non-WSOL trades**, only step 4 differs: the program initializes the maker's ATA with the taker as rent payer. When the limit order closes, the taker is rebated accordingly. * * * WSOL handling[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#wsol-handling) ----------------------------------------------------------------------------------------------------------------------- When the output mint is WSOL and the order will close: * The taker **must pass a seeded (non-ATA) WSOL token account** as the maker's output account. * The program sends SOL to this account and **closes it to the maker**, crediting their wallet balance directly. * The taker wraps SOL into their ATA before the take, and closes the ATA after. * * * TakeOrder accounts[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#takeorder-accounts) --------------------------------------------------------------------------------------------------------------------------------- * `**0**` **—** `**taker**` — Taker wallet. **Writable, signer.** * `**1**` **—** `**maker**` — Maker wallet (receives output tokens on full fill). **Writable.** * `**2**` **—** `**inputMint**` — Input token mint. Read-only. * `**3**` **—** `**outputMint**` — Output token mint. Read-only. * `**4**` **—** `**limitOrder**` — Limit order PDA. **Writable.** * `**5**` **—** `**takerInputMintTokenAccount**` — Taker's input token account (receives input tokens). **Writable.** * `**6**` **—** `**takerOutputMintTokenAccount**` — Taker's output token account (sends output tokens + fees). **Writable.** * `**7**` **—** `**makerOutputMintTokenAccount**` — Maker's output token account (or seeded account for WSOL). **Writable.** * `**8**` **—** `**makerInputMintTokenAccount**` — Maker's input token account (for remaining balance on close). **Writable.** * `**9**` **—** `**feeReceiverTokenAccount**` — Fee receiver's output token account. **Writable.** * `**10**` **—** `**vaultManager**` — Vault manager PDA (`["vault_manager"]`). Read-only. * `**11**` **—** `**inputMintVault**` — Vault's input token account. **Writable.** * `**12**` **—** `**outputMintVault**` — Vault's output token account. **Writable.** * `**13**` **—** `**systemProgram**` — System program. Read-only. * `**14**` **—** `**inputMintProgram**` — Token program for input mint (SPL or SPL-2022). Read-only. * `**15**` **—** `**outputMintProgram**` — Token program for output mint (SPL or SPL-2022). Read-only. * `**16**` **—** `**associatedTokenProgram**` — Associated Token Program. Read-only. * `**17**` **—** `**instructionsSysvar**` — Instructions sysvar. Read-only. * * * Instruction data[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#instruction-data) ----------------------------------------------------------------------------------------------------------------------------- * **Byte 0** — `discriminator` (`u8`) — Always `2` (TakeOrder). * **Bytes 1–8** — `amount` (`u64`, little-endian) — Input token amount to take. * **Bytes 9–16** — `max_cost_amount` (`u64`, little-endian) — Maximum output tokens the taker will pay. **Use** `**u64::MAX**` **for no limit.** * **Byte 17** — `output_mint_token_account_bump` (`u8`) — PDA bump for maker's output token account. * **Byte 18** — `input_mint_token_account_bump` (`u8`) — PDA bump for maker's input token account. * **Byte 19** — `fee_receiver_output_mint_bump` (`u8`) — PDA bump for fee receiver's output token account. * * * Full execution code[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#full-execution-code) ----------------------------------------------------------------------------------------------------------------------------------- The following code shows how to create a complete set of instructions to execute a `TakeOrder`, including setup (ATA creation, WSOL wrapping) and cleanup (WSOL unwrapping). * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#related-pages) ----------------------------------------------------------------------------------------------------------------------- * [Limit Orders Overview](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview) — Order structure, price calculation, time-in-force, fees * [Limit Order Events](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/events) — Event structure and parsing from program logs * [Error Codes](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/error-codes) — Program error codes [PreviousOverview](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview) [NextLimit Order Events](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/events) Last updated 4 months ago * [Fill behavior](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#fill-behavior) * [Full fill](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#full-fill) * [Partial fill](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#partial-fill) * [Example: 100 USDC → 1 SOL with 5 BPS fee](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#example-100-usdc-1-sol-with-5-bps-fee) * [WSOL handling](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#wsol-handling) * [TakeOrder accounts](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#takeorder-accounts) * [Instruction data](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#instruction-data) * [Full execution code](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#full-execution-code) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order#related-pages) Copy /// Fees to be paid to this address pub const FEE_RECEIVER_ADDRESS: Pubkey = pubkey!("Bq5ZzfiU3vTiJPrBJFcr98BnUy9Wc1dg9ASeycB2tX1C"); /// Derives the vault manager address and bump seed. pub fn get_vault_manager_address_and_bump_seed() -> (Pubkey, u8) { Pubkey::find_program_address(&[b"vault_manager"], &TITAN_LIMIT_ORDER_PROGRAM_ID) } /// Derives the associated token address and bump seed for a given wallet and token mint. pub fn get_associated_token_address_and_bump_seed( wallet_address: &Pubkey, token_mint_address: &Pubkey, token_program: &Pubkey, ) -> (Pubkey, u8) { Pubkey::find_program_address( &[\ &wallet_address.to_bytes(),\ &token_program.to_bytes(),\ &token_mint_address.to_bytes(),\ ], &ASSOCIATED_TOKEN_PROGRAM_ID, ) } /// Creates an instruction bundle to create a token account with a seed. pub fn create_token_account_with_seed_instructions( payer: &Pubkey, authority: &Pubkey, mint: &Pubkey, seed: &str, owner: &Pubkey, ) -> Result<(Pubkey, Vec), TitanSDKError> { let token_account = Pubkey::create_with_seed(payer, seed, owner) .map_err(|_| TitanSDKError::FailedToCreateInstruction)?; // Get minimum balance for rent exemption let token_account_space = spl_token::state::Account::LEN; let lamports = 2039280u64; // Create account with seed instruction let create_account_ix = create_account_with_seed( payer, &token_account, payer, seed, lamports, token_account_space as u64, owner, ); // Initialize token account instruction let init_account_ix = initialize_account(&spl_token::id(), &token_account, mint, authority) .map_err(|_| TitanSDKError::FailedToCreateInstruction)?; Ok((token_account, vec![create_account_ix, init_account_ix])) } /// Discriminator for TakeOrder instruction. mod TakeOrder { pub const DISCRIMINATOR: u8 = 2; } /// Represents a bundle of instructions, including setup instructions and the main instruction. pub struct InstructionBundle { /// A vector of setup instructions that need to be executed before the main instruction. pub setup: Vec, /// The main instruction to be executed. pub instruction: Instruction, /// Cleanup instructions to be executed after the main instruction. pub cleanup: Vec, } pub fn create_take_order_instruction( // Taker of the order, signer. taker: Pubkey, // Input mint token account taker_input_mint_token_account: Pubkey, // Output mint token account w/ taker authority taker_output_mint_token_account: Pubkey, // Limit order state. limit_order: &LimitOrder, // Input mint amount to recieve amount: u64, // Max cost taken from output mint token account // If this is breached the ixn will fail. max_cost_limit: Option, // Input token program [spl / spl-2022] input_mint_program: Pubkey, // Output token program [spl / spl-2022] output_mint_program: Pubkey, ) -> Result { let time_in_force = TimeInForce::try_from(limit_order.time_in_force)?; let max_cost_limit = max_cost_limit.unwrap_or(u64::MAX); let remaining_balance_left = amount != limit_order.get_remaining_amount(); let order_will_close = !remaining_balance_left || time_in_force == TimeInForce::ImmediateOrCancel || time_in_force == TimeInForce::TakeCancelsOrder; let fee_ticks = limit_order.fee_ticks; let (cost, fee) = limit_order .calculate_costs_and_fee(amount, fee_ticks)?; let output_is_wsol = limit_order.output_mint.eq(&WRAPPED_SOL); let input_is_wsol = limit_order.input_mint.eq(&WRAPPED_SOL); let limit_order_address = derive_limit_order_address(limit_order); let (vault_manager_address, _) = get_vault_manager_address_and_bump_seed(); let maker = Pubkey::new_from_array(limit_order.maker); let input_mint = Pubkey::new_from_array(limit_order.input_mint); let output_mint = Pubkey::new_from_array(limit_order.output_mint); let mut setup = vec![create_associated_token_account_idempotent(\ &taker,\ &Pubkey::new_from_array(FEE_RECEIVER_ADDRESS),\ &output_mint,\ &output_mint_program,\ )]; let mut cleanup = vec![]; if output_is_wsol { let wsol_ata = get_associated_token_address_with_program_id( &taker, &output_mint, &output_mint_program, ); setup.extend_from_slice(&[\ create_associated_token_account_idempotent(\ &taker,\ &taker,\ &output_mint,\ &output_mint_program,\ ),\ transfer(&taker, &wsol_ata, cost.saturating_add(fee)),\ sync_native(&output_mint_program, &wsol_ata)?,\ ]); cleanup.push( close_account(&output_mint_program, &wsol_ata, &taker, &taker, &[])?, ) } let (output_mint_token_account_address, output_mint_token_account_bump) = if order_will_close && output_is_wsol { // Handle the special case here let (pk, instructions) = create_token_account_with_seed_instructions( &taker, &taker, &output_mint, "token_seed", &output_mint_program, )?; setup.extend(instructions); (pk, 0) // Bump is not used in this case } else { // otherwise always assume its the makers output ata. get_associated_token_address_and_bump_seed(&maker, &output_mint, &output_mint_program) }; let (input_mint_token_account_address, input_mint_token_account_bump) = if order_will_close && remaining_balance_left && input_is_wsol { // Handle the special case here let (pk, instructions) = create_token_account_with_seed_instructions( &taker, &taker, &input_mint, "token_seed", &input_mint_program, )?; setup.extend(instructions); (pk, 0) // Bump is not used in this case } else { // otherwise always assume its the makers output ata. get_associated_token_address_and_bump_seed(&maker, &input_mint, &input_mint_program) }; let (input_mint_vault_address, _) = get_associated_token_address_and_bump_seed( &vault_manager_address, &input_mint, &input_mint_program, ); let (output_mint_vault_address, _) = get_associated_token_address_and_bump_seed( &vault_manager_address, &output_mint, &output_mint_program, ); // Create the vault manager output token account if it doesn't exist setup.push(create_associated_token_account_idempotent( &taker, &vault_manager_address, &output_mint, &output_mint_program, )); let fee_receiver = Pubkey::new_from_array(FEE_RECEIVER_ADDRESS); let (fee_receiver_output_mint_token_account, fee_reciever_output_mint_bump) = get_associated_token_address_and_bump_seed( &fee_receiver, &output_mint, &output_mint_program, ); let mut data = vec![*instructions::TakeOrder::DISCRIMINATOR]; data.extend_from_slice(&amount.to_le_bytes()); data.extend_from_slice(&max_cost_limit.to_le_bytes()); data.extend_from_slice(&[\ output_mint_token_account_bump,\ input_mint_token_account_bump,\ fee_reciever_output_mint_bump,\ ]); let accounts = vec![\ AccountMeta::new(taker, true),\ AccountMeta::new(maker, false),\ AccountMeta::new_readonly(input_mint, false),\ AccountMeta::new_readonly(output_mint, false),\ AccountMeta::new(limit_order_address, false),\ AccountMeta::new(taker_input_mint_token_account, false),\ AccountMeta::new(taker_output_mint_token_account, false),\ AccountMeta::new(output_mint_token_account_address, false),\ AccountMeta::new(input_mint_token_account_address, false),\ AccountMeta::new(fee_receiver_output_mint_token_account, false),\ AccountMeta::new_readonly(vault_manager_address, false),\ AccountMeta::new(input_mint_vault_address, false),\ AccountMeta::new(output_mint_vault_address, false),\ AccountMeta::new_readonly(solana_program::system_program::ID, false),\ AccountMeta::new_readonly(input_mint_program, false),\ AccountMeta::new_readonly(output_mint_program, false),\ AccountMeta::new_readonly(ASSOCIATED_TOKEN_PROGRAM_ID, false),\ AccountMeta::new_readonly(solana_program::sysvar::instructions::ID, false),\ ]; Ok(InstructionBundle { setup, instruction: Instruction { program_id: TITAN_LIMIT_ORDER_PROGRAM_ID, accounts, data, }, cleanup, }) } --- # Types Reference | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types.md) . All type definitions live inline on the page where they're used. This page is an index for quick navigation. **Field names are** `**camelCase**` **on the wire (MessagePack) unless otherwise specified.** Rust SDK types use `snake_case` with serde renaming. * * * Wire protocol & common types[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#wire-protocol-and-common-types) ---------------------------------------------------------------------------------------------------------------------------------------------- Defined on [Wire Protocol](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol) : * `**Pubkey**` — 32-byte Solana public key * `**AccountMeta**` — compact account descriptor (`p`, `s`, `w`) * `**Instruction**` — on-chain instruction (`p`, `a`, `d`) * `**SwapMode**` — `ExactIn` or `ExactOut` * `**ClientRequest**` — request envelope with `id` and `data` * `**ServerMessage**` — `Response`, `Error`, `StreamData`, or `StreamEnd` * `**ResponseSuccess**` / `**ResponseError**` — success and error response types * `**StreamData**` / `**StreamEnd**` / `**StreamStart**` — stream lifecycle types * * * Server info types[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#server-info-types) ---------------------------------------------------------------------------------------------------------------------- Defined on [GetInfo](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info) : * `**ServerInfo**` — protocol version and server settings * `**VersionInfo**` — `major`, `minor`, `patch` * `**ServerSettings**` — quote update, swap, transaction, and connection settings * `**BoundedValueWithDefault**` — `min`, `max`, `default` * `**QuoteUpdateSettings**` / `**SwapSettings**` / `**TransactionSettings**` / `**ConnectionSettings**` * * * Request types[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#request-types) -------------------------------------------------------------------------------------------------------------- Defined on [NewSwapQuoteStream](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream) : * `**SwapQuoteRequest**` — `swap` + `transaction` + `update` * `**SwapParams**` — input/output mints, amount, slippage, routing filters * `**TransactionParams**` — wallet key, fee config, token account options * `**QuoteUpdateParams**` — stream interval and quote count * * * Quote & stream data types[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#quote-and-stream-data-types) ---------------------------------------------------------------------------------------------------------------------------------------- Defined on [NewSwapQuoteStream](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#stream-updates) : * `**SwapQuotes**` — quote batch with provider-keyed `quotes` map * `**SwapRoute**` — single route with instructions, ALTs, expiry, compute budget * `**RoutePlanStep**` — one hop in a multi-step route * `**PlatformFee**` — `amount` and `fee_bps` * * * Discovery types[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#discovery-types) ------------------------------------------------------------------------------------------------------------------ Defined on [GetVenues / ListProviders](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers) : * `**VenueInfo**` — venue labels and optional program IDs * `**ProviderInfo**` — provider id, name, kind, optional icon * `**ProviderKind**` — `"DexAggregator"` or `"RFQ"` * * * Stop stream types[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#stop-stream-types) ---------------------------------------------------------------------------------------------------------------------- Defined on [StopStream](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream) : * `**StopStreamRequest**` — stream ID to stop * `**StopStreamResponse**` — confirmed stream ID * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#related-pages) -------------------------------------------------------------------------------------------------------------- * [Wire Protocol](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol) — encoding rules, common types, and message envelope types * [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection) — WebSocket setup and authentication * [NewSwapQuoteStream](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream) — request, response, and stream data types * [GetInfo](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info) — server info and settings types * [GetVenues / ListProviders](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers) — discovery types * [StopStream](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream) — stop stream types [PreviousWire Protocol](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol) [NextError Codes](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes) Last updated 4 months ago * [Wire protocol & common types](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#wire-protocol-and-common-types) * [Server info types](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#server-info-types) * [Request types](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#request-types) * [Quote & stream data types](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#quote-and-stream-data-types) * [Discovery types](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#discovery-types) * [Stop stream types](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#stop-stream-types) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#related-pages) --- # Withdrawals | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/withdrawals.md) . Funds in a user's manager always come back to that user's own external wallet — the manager's policy allows nothing else. There are two ways to move them, both following the same intent → sign → confirm pattern as order creation. Wallet-level withdrawals[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/withdrawals#wallet-level-withdrawals) ---------------------------------------------------------------------------------------------------------------------------------------------- These pull a chosen token out of the manager regardless of any order. Use them to power a "withdraw available balance" action. Check what's available first: Copy const { data } = await callTitanDca('/me/balance?hideZero=true', { sub }); // each balance row: totalBalance, lockedForFutureTxns, withdrawalPending, availableToWithdraw `availableToWithdraw` is `max(total − locked − withdrawalPending, 0)` — the source of truth for a "withdraw max" button. Active DCA orders lock their unspent input (`totalAmount − amountSpent`), so it won't be available until the order ends. Build the withdrawal tx, omitting `amount` to withdraw the max: Copy const intent = await callTitanDca('/withdraw/transaction', { method: 'POST', sub, body: { userPubkey, // fee payer & destination tokenMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', amount: '400000', // optional; omit for max }, }); // user signs intent.data.transaction, then: await callTitanDca('/withdraw/confirm', { method: 'POST', sub, body: { signedTransaction }, }); Titan re-runs the lock-aware check at confirm time using the `(mint, amount)` from the unsigned transaction it returned — so if that transaction goes stale during its 5-minute window (e.g. another withdrawal landed in the meantime), it's rejected before broadcast rather than overdrawing. HTTP `error.code` When 400 `INSUFFICIENT_AVAILABLE` The whole balance is locked, or the wallet is empty. 400 `FUNDS_LOCKED` `requested > available` — some balance is locked by orders or a pending withdrawal. `details` has the breakdown. 400 `INSUFFICIENT_BALANCE` `requested > chainBalance` — the wallet genuinely doesn't hold that much. 409 `ONBOARDING_INCOMPLETE` Manager not fully provisioned. Re-call onboard, then retry. Order-level withdrawals[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/withdrawals#order-level-withdrawals) -------------------------------------------------------------------------------------------------------------------------------------------- These return the funds tied to a single terminal order (`completed` / `cancelled` / `failed`) back to the external wallet. `**failed**` **orders return automatically** — Titan sends their unspent input back without any call from you (see [Lifecycle](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle#automatic-input-return) ). So order-level withdrawal is mainly for `completed` and `cancelled` orders; on a failed order it usually reports `NOTHING_TO_WITHDRAW` or `ALREADY_WITHDRAWN`. `POST /orders/{orderId}/withdraw` is safe to retry — each call rebuilds a fresh tx (so the user can re-prompt their wallet) and resets the inactivity timer on the automatic recovery process. `withdrawalAmounts` enumerates the per-mint amounts the transaction will move, one entry per non-zero mint, which is what you show the user before they sign. HTTP `error.code` When 400 `NOTHING_TO_WITHDRAW` No order-owned funds remain (the user likely drained them via a wallet-level withdrawal). 400 `INVALID_STATE` Order isn't `completed` / `cancelled` / `failed`. 400 `ALREADY_WITHDRAWN` A prior withdrawal already completed — including the automatic return on a failed order. 409 `EXECUTION_IN_FLIGHT` A swap attempt is still being reconciled. Self-resolving — retry shortly. 503 `RPC_UNAVAILABLE` Couldn't fetch on-chain balance. Retry shortly. ### Releasing a stuck withdrawal lock[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/withdrawals#releasing-a-stuck-withdrawal-lock) If the user dismisses the wallet prompt from `POST /orders/{orderId}/withdraw`, the order sits in `withdrawalStatus: pending`. Call `POST /orders/{orderId}/withdraw/abandon` to make the "Withdraw" button usable again immediately — it's idempotent. Without it, an automatic server-side process clears the lock within about 3 minutes anyway. Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/withdrawals#related-pages) ------------------------------------------------------------------------------------------------------------------------ * [Create & Manage Orders](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders) — cancel-with-withdraw builds an order-level withdrawal in one call * [Lifecycle & Polling](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle) — `withdrawalStatus` transitions and automatic input return * [Error Codes](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes) — the full withdrawal error catalog [PreviousCreate & Manage Orders](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders) [NextPlatform Fees](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/platform-fees) Last updated 1 month ago * [Wallet-level withdrawals](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/withdrawals#wallet-level-withdrawals) * [Order-level withdrawals](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/withdrawals#order-level-withdrawals) * [Releasing a stuck withdrawal lock](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/withdrawals#releasing-a-stuck-withdrawal-lock) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/withdrawals#related-pages) Copy const intent = await callTitanDca(`/orders/${orderId}/withdraw`, { method: 'POST', sub }); // intent.data.withdrawalAmounts → [{ mint, amount }, …] — preview before the user signs await callTitanDca(`/orders/${orderId}/withdraw/confirm`, { method: 'POST', sub, body: { signedTransaction }, }); --- # Limits & Idempotency | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits.md) . Limits[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits#limits) -------------------------------------------------------------------------------------------------------- Limit Value List endpoints At most **500 rows**, unless stated otherwise. Use `createdAtGte` / `createdAtLte` on `/partners/me/*` for older data. `GET /orders/pending/history` **50 rows** — the one exception to the 500 cap. SIWS message Max **1024 bytes**; signed bytes must match the canonical form exactly (LF endings, trailing `\n` after `Nonce:`); `Issued At` within **±10 minutes**. Cycle frequency Minimum **60 seconds** (`cycleFrequencySeconds`). Per-cycle value `amountPerCycle` must be worth at least **$10 USD** by default (configurable per tenant). Enforced at create and whenever `amountPerCycle` changes. USDC/USDT count as $1.00; other mints are priced by the oracle at request time. Pending order TTL **5 minutes** between `intent` and `confirm`. Modification lock **30 seconds** between a modify-intent returning `requiresTransaction: true` and `modify/confirm`. Idempotency replay window **24 hours**. Only `2xx` responses are cached. Order-level withdrawal auto-recovery Clears a stuck `withdrawalStatus: pending` roughly **3 minutes** after it gets stuck. API key environment A key is bound to exactly one environment (`staging` or `production`). Rate limits Configured per partner during onboarding. Idempotency[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits#idempotency) ------------------------------------------------------------------------------------------------------------------ Every authenticated `POST` under the user-scoped tier accepts an `X-Idempotency-Key` header — `/orders/intent`, `/orders/confirm`, `/dca/{id}/modify/confirm`, `/orders/{id}/cancel`, `/orders/{id}/withdraw`, `/orders/{id}/withdraw/confirm`, `/orders/{id}/withdraw/abandon`, `/orders/{id}/pause`, `/orders/{id}/resume`, `/orders/{id}/retry`, `/withdraw/transaction`, and `/withdraw/confirm`. The header is optional — omit it on first-fire calls; include it on calls you intend to retry. `POST /partner/onboard` doesn't need a key — it's inherently idempotent, since re-calling with the same `sub` replays the same `userId` / `walletAddress`. Idempotency isn't applied to GET endpoints or partner-only endpoints. **The rules:** * A key is scoped per **(your tenant, end-user, key string)** — two different users sharing a key value don't collide. * Same key **\+ same body** → Titan replays the exact cached JSON response (with the original `2xx` status) for **24 hours**. * Same key **\+ different body** → `422 IDEMPOTENCY_KEY_REUSED`. Choose a fresh key. * **Only** `**2xx**` **responses are cached.** If the first call returns `4xx` or `5xx`, the key isn't recorded — a retry with the same key runs the handler again. A failed `/orders/intent` (e.g. a bad mint) shouldn't be permanently bound to a request body. Use a deterministic, client-side key per logical user action — `dca:create::v1`. Don't randomize per network attempt; the whole point is that retries hash to the same key. Polling, not webhooks[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits#polling-not-webhooks) ------------------------------------------------------------------------------------------------------------------------------------- Titan DCA doesn't push state changes — there are no webhooks today. Poll the read endpoints while a user is on a DCA surface, and follow the cadence guidance in [Lifecycle & Polling](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle) . Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits#related-pages) ---------------------------------------------------------------------------------------------------------------------- * [Create & Manage Orders](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders) — where idempotency keys matter most * [Error Codes](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes) — `IDEMPOTENCY_KEY_REUSED` and the retryable codes * [Lifecycle & Polling](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle) — polling cadence and back-off [PreviousError Codes](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes) [NextOverview](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview) Last updated 1 month ago * [Limits](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits#limits) * [Idempotency](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits#idempotency) * [Polling, not webhooks](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits#polling-not-webhooks) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits#related-pages) --- # Error Handling & Reconnect | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling.md) . Titan Direct is a persistent WebSocket connection. Connections drop, tokens expire, and streams end unexpectedly. This guide covers every failure mode and how to build a reconnect loop that keeps your integration running. Server messages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling#server-messages) ------------------------------------------------------------------------------------------------------------------------ The server sends one of four message types — `Response`, `Error`, `StreamData`, or `StreamEnd`. See [Types Reference](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) for the full type definitions. Server errors[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling#server-errors) -------------------------------------------------------------------------------------------------------------------- When a request fails, the server sends an [`Error`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) instead of a `Response`. Check for the `Error` key in every message handler: Copy ws.on('message', async (raw: Buffer) => { const msg = await decodeMessage(raw); if ('Error' in msg) { const { code, message, requestId } = msg.Error; console.error(`Request ${requestId} failed — code ${code}: ${message}`); return; } }); Stream errors[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling#stream-errors) -------------------------------------------------------------------------------------------------------------------- A stream can end abnormally. [`StreamEnd`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) carries an optional `errorCode` and `errorMessage` — if present, the stream did not terminate cleanly: Connection drops[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling#connection-drops) -------------------------------------------------------------------------------------------------------------------------- When the WebSocket closes, all active streams are dead. Listen for the `close` event and trigger your reconnect logic: Token expiry[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling#token-expiry) ------------------------------------------------------------------------------------------------------------------ The server refuses connections where the JWT `exp` claim is in the past. If your connection is rejected immediately, check that your token is still valid before reconnecting: Refresh your token before calling `connect()` if it's close to expiry. Reconnect with exponential backoff[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling#reconnect-with-exponential-backoff) -------------------------------------------------------------------------------------------------------------------------------------------------------------- The SDK has no built-in reconnect — it's your responsibility. Here's a complete reconnect loop: Stream IDs do not survive reconnects. After reconnecting, you must open a new stream — the previous stream ID is no longer valid. Best practices[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling#best-practices) ---------------------------------------------------------------------------------------------------------------------- * Call [`GetInfo`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info) after every reconnect to confirm the server is reachable before opening streams. * If a route has `expiresAtMs` or `expiresAfterSlot` set, check these before building a transaction — a quote valid when received can go stale by the time it lands on-chain. * Keep your reconnect loop separate from your quote processing logic so a stream error doesn't silently kill the reconnect handler. * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling#related-pages) -------------------------------------------------------------------------------------------------------------------- * [Stream & Execute a Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute) — full guide with transaction building and error handling * [Error Codes](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes) — numeric error code reference * [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection) — protocol negotiation details * [Types Reference](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) — `ServerMessage`, `ResponseError`, `StreamEnd`, and all type definitions [PreviousTransaction Template](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template) [NextAPI Reference](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference) Last updated 4 months ago * [Server messages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling#server-messages) * [Server errors](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling#server-errors) * [Stream errors](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling#stream-errors) * [Connection drops](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling#connection-drops) * [Token expiry](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling#token-expiry) * [Reconnect with exponential backoff](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling#reconnect-with-exponential-backoff) * [Best practices](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling#best-practices) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling#related-pages) Copy ws.on('message', async (raw: Buffer) => { const msg = await decodeMessage(raw); if ('StreamEnd' in msg) { const { id, errorCode, errorMessage } = msg.StreamEnd; if (errorCode !== undefined) { console.error(`Stream ${id} ended with error ${errorCode}: ${errorMessage}`); // Restart the stream or reconnect } else { console.log(`Stream ${id} ended cleanly`); } } }); Copy ws.on('close', (code: number, reason: Buffer) => { console.warn(`Connection closed — code: ${code}, reason: ${reason.toString()}`); if (code !== 1000) { // Abnormal close — reconnect scheduleReconnect(); } }); ws.on('error', (err: Error) => { console.error('WebSocket error:', err.message); // 'close' will fire after this }); Copy function isTokenExpired(token: string): boolean { const [, payload] = token.split('.'); const claims = JSON.parse(Buffer.from(payload, 'base64').toString()); return Date.now() / 1000 > claims.exp; } Copy import WebSocket from 'ws'; import { Encoder, Decoder } from '@msgpack/msgpack'; import bs58 from 'bs58'; import { zstdCompress, zstdDecompress } from 'http-encoding'; const encoder = new Encoder({ useBigInt64: true }); const decoder = new Decoder({ useBigInt64: true }); const BASE_DELAY_MS = 1_000; const MAX_DELAY_MS = 30_000; let useCompression = false; async function sendRequest(ws: WebSocket, id: number, data: Record) { const encoded = encoder.encode({ id, data }); ws.send(useCompression ? await zstdCompress(encoded) : encoded); } async function decodeMessage(raw: Buffer): Promise { const data = useCompression ? await zstdDecompress(raw) : raw; return decoder.decode(data); } async function connect(): Promise { const url = `${process.env.TITAN_ENDPOINT}?auth=${process.env.TITAN_API_KEY}`; return new Promise((resolve, reject) => { const ws = new WebSocket(url, [\ 'v1.api.titan.ag+zstd',\ 'v1.api.titan.ag',\ ]); ws.once('open', () => { useCompression = ws.protocol !== 'v1.api.titan.ag'; resolve(ws); }); ws.once('error', reject); }); } async function runWithReconnect() { let attempt = 0; while (true) { try { const ws = await connect(); console.log('Connected'); attempt = 0; // reset backoff on success // Set up your message handler and streams here await setupStreams(ws); // Wait for connection to close await new Promise((resolve) => ws.once('close', resolve)); } catch (err) { attempt++; const delay = Math.min(BASE_DELAY_MS * 2 ** (attempt - 1), MAX_DELAY_MS); console.warn(`Reconnect attempt ${attempt} in ${delay}ms...`); await new Promise((resolve) => setTimeout(resolve, delay)); } } } async function setupStreams(ws: WebSocket) { let requestId = 0; // Call GetInfo first to confirm connection sendRequest(ws, requestId++, { GetInfo: {} }); ws.on('message', async (raw: Buffer) => { const msg = await decodeMessage(raw); if ('Response' in msg && 'GetInfo' in msg.Response.data) { // Connection confirmed — open your streams openQuoteStream(ws, requestId++); } if ('Error' in msg) { console.error(`Error ${msg.Error.code}: ${msg.Error.message}`); } if ('StreamEnd' in msg && msg.StreamEnd.errorCode !== undefined) { console.error(`Stream error: ${msg.StreamEnd.errorMessage}`); ws.close(); // trigger reconnect } }); } async function openQuoteStream(ws: WebSocket, id: number) { const SOL = bs58.decode('So11111111111111111111111111111111111111112'); const USDC = bs58.decode('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); sendRequest(ws, id, { NewSwapQuoteStream: { swap: { inputMint: SOL, outputMint: USDC, amount: 1_000_000_000n, slippageBps: 50, }, transaction: { userPublicKey: bs58.decode('YOUR_WALLET_PUBLIC_KEY'), }, }, }); } runWithReconnect(); --- # Authentication | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication.md) . The DCA Partner API uses two headers. There's no OAuth, no token exchange, and no partner-signed JWT — your backend holds one API key and identifies users by the same id you already use for them. The only signature anywhere in the flow is the user's one-time SIWS at [onboarding](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding) . Header Required on Purpose `X-Titan-Key` **every** request Authenticates your integration (your tenant). Keep it server-side; never ship it to a browser. `X-Titan-User` every **user-scoped** request Your stable id for the end-user — the same value you onboarded as `sub`. Titan maps it to that user's manager. Every header originates on your backend. Nothing goes from the end-user's browser directly to Titan. Header Value `X-Titan-Key` Your partner API key. Never expose to browsers. `X-Titan-User` Your stable id for the user the request acts on. Required on user-scoped routes. `Content-Type` `application/json` on POST/PATCH. `X-Idempotency-Key` Optional on selected POSTs. A stable, unique-per-operation string. See [Idempotency](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits#idempotency) . `X-Request-Id` Optional. If set, Titan keeps it in logs and echoes it back as `x-request-id`. Otherwise one is generated. Request tiers[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication#request-tiers) ------------------------------------------------------------------------------------------------------------------------------ Tier Headers Endpoints **Public** none `GET /health` **Partner-only** `X-Titan-Key` `POST /partner/onboard`, `GET /partners/me/*` (reporting) **User-scoped** `X-Titan-Key` + `X-Titan-User` Everything that acts on a specific user — balances, orders, withdrawals, `GET /me`. Reporting is **tenant-scoped, not user-header-scoped**. The `/partners/me/*` endpoints use `X-Titan-Key` only and return rows across your whole tenant. To narrow them to one user, pass `?userId=` — that `userId` is the opaque Titan id, **not** your `sub` / `X-Titan-User`. This is the main reason to store the `userId` onboarding returns. User consent[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication#user-consent) ---------------------------------------------------------------------------------------------------------------------------- A user's external wallet is bound to their manager by a SIWS signature collected once at `POST /partner/onboard`. After that, you act on their behalf within the on-chain policy by sending `X-Titan-User` — no further per-request user signature. The policy pins the manager to DCA swaps and withdrawals only to that user's own external wallet, so neither you nor Titan can move funds anywhere else. Auth errors[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication#auth-errors) -------------------------------------------------------------------------------------------------------------------------- These apply to every authenticated endpoint: HTTP `error.code` When 401 `INVALID_API_KEY` Missing or unknown `X-Titan-Key`. 401 `KEY_REVOKED` Your key was revoked. 401 `ENV_MISMATCH` Key issued for a different environment. 401 `UNAUTHORIZED` On a user-scoped route: `X-Titan-User` is missing, or the supplied id was never onboarded. Call `POST /partner/onboard` for that user first. 403 `PRODUCT_DISABLED` The DCA product is disabled on your key. 403 `PRODUCT_EXPIRED` The DCA grant has expired. 403 `TENANT_SUSPENDED` Your tenant is suspended (recoverable; contact Titan). 403 `TENANT_DELETED` Your tenant is deleted. 403 `TENANT_NOT_PROVISIONED` Key valid, but no tenant row yet — onboarding step missing on Titan's side. Verify your plumbing[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication#verify-your-plumbing) -------------------------------------------------------------------------------------------------------------------------------------------- `GET /me` resolves the identity for the current request and is the quickest smoke test that `X-Titan-Key` + `X-Titan-User` map to the user you expect: `sessionId` is empty for partner integrations — it's a browser-session field with no server-side equivalent. Rely on `userId` / `walletAddress`, and treat both as opaque strings (the field names are stable; their format may change). Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication#related-pages) ------------------------------------------------------------------------------------------------------------------------------ * [Onboarding (SIWS)](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding) — how the `X-Titan-User` → manager mapping gets created * [Endpoints](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints) — the tier each route belongs to * [Error Codes](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes) — the complete error catalog [PreviousAPI Reference](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference) [NextEndpoints](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints) Last updated 1 month ago * [Request tiers](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication#request-tiers) * [User consent](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication#user-consent) * [Auth errors](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication#auth-errors) * [Verify your plumbing](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication#verify-your-plumbing) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication#related-pages) Copy { "success": true, "data": { "userId": "", "walletAddress": "GZk2v…", "sessionId": "" } } --- # Transaction Template | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template.md) . When you build a swap transaction, you usually want to add instructions of your own — a compute-budget setting, a memo, a custom fee transfer, an oracle update, an app-specific log. The problem: every byte you add eats into the **1232-byte Solana transaction limit**, and the route Titan returned may no longer fit. `transactionTemplate` solves this. You tell Titan upfront what extra instructions, ALTs, and account metas will share the final transaction with the swap. Titan then sizes the route so the **assembled** transaction fits inside Solana's limits when you splice everything together. `**transactionTemplate**` **is incompatible with** `**accountsLimitTotal**`**,** `**accountsLimitWritable**`**, and** `**sizeConstraint**`**.** The template **is** the sizing constraint — passing both returns an error. When to use it[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#when-to-use-it) ---------------------------------------------------------------------------------------------------------------------------- * You're prepending or appending instructions that aren't part of the swap (compute-budget settings, memos, app-specific logs, oracle pokes, custom fee transfers). * You're using your own ALTs (rebate program, fee program, app-specific routing). * You're hitting tx-size errors after combining the route with your own instructions. For everything else, the default sizing (`accountsLimitTotal` / `accountsLimitWritable`) is enough. Pair with V3[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#pair-with-v3) ------------------------------------------------------------------------------------------------------------------------ **Use** `**titanSwapVersion: 3**` **whenever you use** `**transactionTemplate**`**.** V3 manages input and output token accounts internally, so the router does **not** insert ATA create/close instructions around the swap — the full residual byte budget goes to the route. With V2, the router still adds wSOL wrap/unwrap and ATA-creation instructions, which makes templates less predictable. `**titanSwapVersion**` **is the integer** `**3**`**, not the string** `**"V3"**`**.** Apollo rejects strings with `Failed to deserialize query string: titanSwapVersion: invalid digit found in string`. The template[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#the-template) ------------------------------------------------------------------------------------------------------------------------ See [`TransactionTemplate`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#transaction-template) for the full struct. Three fields: * `**i**` — Instructions that will sit in the final transaction **before** the swap. Include any ATA creation/deletion for the input and output mints yourself if you need them — the template doesn't assume the router will add them. (V3 doesn't need them; V2 might.) * `**a**` — ALTs the surrounding transaction already references. **Order matters** — Solana resolves ALTs greedily. Provide them in the order you'll use when compiling the message. Titan extends this array with any ALTs it uses for the swap. * `**m**` — Extra account metas that belong to the surrounding transaction but aren't reachable from any instruction in `i`. Rare — leave empty unless you need it. ### Wire format[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#wire-format) The MessagePack wire format uses **single-letter field names** for space efficiency. Type Wire field Meaning `TransactionTemplate` `i` `instructions` (array) `TransactionTemplate` `a` `alts` (array) `TransactionTemplate` `m` `accountMetas` (array) `Instruction` `p` `programId` (32-byte pubkey) `Instruction` `a` `accounts` (array of `AccountMeta`) `Instruction` `d` `data` (raw bytes) `AccountMeta` `p` `pubkey` (32-byte pubkey) `AccountMeta` `s` `isSigner` (bool) `AccountMeta` `w` `isWritable` (bool) `AddressLookupTableAccount` `p` `key` (32-byte ALT account address) `AddressLookupTableAccount` `a` `addresses` (array of 32-byte pubkeys _inside_ the ALT, in order) **All pubkeys and instruction** `**data**` **are raw byte arrays, not Base58 or Base64 strings.** REST vs WebSocket transport[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#rest-vs-websocket-transport) ------------------------------------------------------------------------------------------------------------------------------------------------------ The template payload is the same shape on both transports, but the wrapping is different: * **REST (Gateway):** MessagePack-encode the template, then **Base64-encode the bytes**, and pass as the `transactionTemplate` query-string value. * **WebSocket (Direct):** Embed the template **inline** inside the `swap` object of your `NewSwapQuoteStream` request. The whole frame is MessagePack already — no Base64 wrapping. Example: V3 + compute-budget template[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#example-v3--compute-budget-template) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ A USDC → SOL swap on V3 with a minimal template containing only the two compute-budget instructions. This is the canonical real-world pattern. Titan Gateway (REST) Titan Direct (WebSocket) On the WebSocket transport, the whole frame is already MessagePack — embed the template **inline** in the request body, no Base64 wrapping needed. What changes in the route[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#what-changes-in-the-route) -------------------------------------------------------------------------------------------------------------------------------------------------- Without a template, Titan sizes routes assuming the swap is the only thing in the transaction — up to the server's defaults (currently 1168 bytes, 64 accounts). With a template, Titan **subtracts the template's footprint** from those budgets before choosing a route: * A compute-budget template (14 bytes, 0 accounts) barely shifts routing — V3 routes usually return as a single instruction with the ALTs the router would have used anyway. * A larger template (custom program calls, multiple ALTs, many account metas) pushes the router toward shorter routes — fewer hops, fewer venues — to leave room. Providers that can't fit a route within the remaining budget are silently dropped from the response. * If no provider can fit a route, you get a 404 (Gateway) or an empty `quotes` map (Direct). Pitfalls[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#pitfalls) ---------------------------------------------------------------------------------------------------------------- * **Don't pair with** `**accountsLimitTotal**` **/** `**accountsLimitWritable**` **/** `**sizeConstraint**`**.** The template replaces them. Passing both returns 400. * `**titanSwapVersion**` **is integer** `**3**`**, not string** `**"V3"**`**.** Strings get rejected with `invalid digit found in string`. * **Use wire-format field names.** Long-form names (`programId`, `accounts`, etc.) return `400 Bad Request: missing field 'p'`. * **Splice the template instructions BEFORE the route instructions** when building the final v0 message. The template represents what the surrounding transaction looks like; the swap sits after it. * **ALT order is load-bearing.** Solana resolves ALTs greedily; the first ALT containing an account wins. If your custom ALT and a Titan ALT both contain the same account, ordering decides which gets used. * **Encode binary as bytes, not Base58.** Inside the MessagePack-encoded template, pubkeys and instruction `data` are raw bytes (msgpack `bin`) — don't pre-encode them to strings. Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#related-pages) -------------------------------------------------------------------------------------------------------------------------- * [NewSwapQuoteStream → Transaction Template](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#transaction-template) — type definition * [NewSwapQuoteStream → Swap V3](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#swap-v3) — V3 router details * [Quote Swap (Gateway)](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap) — Gateway swap reference * [Stream & Execute a Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute) — full transaction-building walkthrough * [Configure Routing](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing) — `accountsLimitTotal`, `accountsLimitWritable`, and other size controls [PreviousFee Collection](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection) [NextError Handling & Reconnect](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling) Last updated 2 months ago * [When to use it](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#when-to-use-it) * [Pair with V3](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#pair-with-v3) * [The template](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#the-template) * [Wire format](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#wire-format) * [REST vs WebSocket transport](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#rest-vs-websocket-transport) * [Example: V3 + compute-budget template](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#example-v3--compute-budget-template) * [What changes in the route](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#what-changes-in-the-route) * [Pitfalls](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#pitfalls) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template#related-pages) Copy import { Encoder, decode } from '@msgpack/msgpack'; import bs58 from 'bs58'; import { Connection, MessageV0, PublicKey, TransactionInstruction, VersionedTransaction, } from '@solana/web3.js'; const encoder = new Encoder({ useBigInt64: true }); // --- Step 1: build the two compute-budget instructions --- const CB_PROGRAM_ID = new PublicKey('ComputeBudget111111111111111111111111111111'); function setComputeUnitLimitData(units: number): Uint8Array { // Discriminator 0x02 + u32 LE const data = new Uint8Array(5); data[0] = 0x02; new DataView(data.buffer).setUint32(1, units, true); return data; } function setComputeUnitPriceData(microLamports: bigint): Uint8Array { // Discriminator 0x03 + u64 LE const data = new Uint8Array(9); data[0] = 0x03; new DataView(data.buffer).setBigUint64(1, microLamports, true); return data; } // --- Step 2: assemble the TransactionTemplate using wire-format field names --- const template = { i: [\ { p: CB_PROGRAM_ID.toBytes(), a: [], d: setComputeUnitLimitData(1_400_000) },\ { p: CB_PROGRAM_ID.toBytes(), a: [], d: setComputeUnitPriceData(0n) }, // sub real priority fee in prod\ ], a: [], m: [], }; // --- Step 3: MessagePack-encode, then Base64-encode --- const transactionTemplate = Buffer.from(encoder.encode(template)).toString('base64'); // --- Step 4: send the quote request --- const USDC = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'; const SOL = 'So11111111111111111111111111111111111111112'; const USER = 'Affd7LDkUY9fjWjjQSr9bvises1Cku4WSwdLNGBhgVW3'; const params = new URLSearchParams({ inputMint: USDC, outputMint: SOL, amount: '100000000', // 100 USDC userPublicKey: USER, slippageBps: '50', titanSwapVersion: '3', // integer 3, not "V3" simulate: 'false', transactionTemplate, }); const res = await fetch( `${process.env.TITAN_ENDPOINT}/api/v1/quote/swap?${params}`, { headers: { 'Authorization': `Bearer ${process.env.TITAN_API_KEY}`, 'Accept': 'application/vnd.msgpack', }, } ); const quotes = decode(new Uint8Array(await res.arrayBuffer())) as any; const winner = quotes.metadata?.ExpectedWinner; const route = winner && quotes.quotes[winner]; // --- Step 5: assemble [templateInstructions, ...routeInstructions] --- function titanIxToTransactionIx(ix: any): TransactionInstruction { return new TransactionInstruction({ programId: new PublicKey(ix.p), data: Buffer.from(ix.d), keys: ix.a.map((a: any) => ({ pubkey: new PublicKey(a.p), isSigner: a.s, isWritable: a.w, })), }); } const templateIxs = template.i.map(titanIxToTransactionIx); const swapIxs = route.instructions.map(titanIxToTransactionIx); const allIxs = [...templateIxs, ...swapIxs]; // Compile into a v0 message with the route's ALTs, sign and send Copy import WebSocket from 'ws'; import { Encoder, decode } from '@msgpack/msgpack'; import { PublicKey } from '@solana/web3.js'; const encoder = new Encoder({ useBigInt64: true }); const CB_PROGRAM_ID = new PublicKey('ComputeBudget111111111111111111111111111111'); function setComputeUnitLimitData(units: number) { const data = new Uint8Array(5); data[0] = 0x02; new DataView(data.buffer).setUint32(1, units, true); return data; } function setComputeUnitPriceData(microLamports: bigint) { const data = new Uint8Array(9); data[0] = 0x03; new DataView(data.buffer).setBigUint64(1, microLamports, true); return data; } const template = { i: [\ { p: CB_PROGRAM_ID.toBytes(), a: [], d: setComputeUnitLimitData(1_400_000) },\ { p: CB_PROGRAM_ID.toBytes(), a: [], d: setComputeUnitPriceData(0n) },\ ], a: [], m: [], }; const ws = new WebSocket( `${process.env.TITAN_WS_ENDPOINT}/api/v1/ws`, ['v1.api.titan.ag'], { headers: { Authorization: `Bearer ${process.env.TITAN_API_KEY}` } }, ); ws.on('open', () => { ws.send(encoder.encode({ id: 1, data: { NewSwapQuoteStream: { swap: { inputMint: new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v').toBytes(), outputMint: new PublicKey('So11111111111111111111111111111111111111112').toBytes(), amount: 100_000_000n, slippageBps: 50, providers: ['Metis', 'Titan'], transactionTemplate: template, // inline, no Base64 }, transaction: { userPublicKey: new PublicKey('Affd7LDkUY9fjWjjQSr9bvises1Cku4WSwdLNGBhgVW3').toBytes(), titanSwapVersion: 3, // integer 3 }, update: { intervalMs: 60_000, numQuotes: 1 }, }, }, })); }); ws.on('message', (raw) => { const msg = decode(raw as Uint8Array) as any; if ('StreamData' in msg && 'SwapQuotes' in msg.StreamData.payload) { const quotes = msg.StreamData.payload.SwapQuotes; const winner = quotes.metadata?.ExpectedWinner; const route = winner && quotes.quotes[winner]; // Assemble [template.i, ...route.instructions] as in the REST tab } }); --- # Configure Routing | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing.md) . Titan routes through [Argos](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/introduction) across all available venues and providers by default. You can restrict or filter routing using the parameters below — all go into the `swap` or `transaction` object of your request. Routing parameters[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing#routing-parameters) --------------------------------------------------------------------------------------------------------------------------------- These fields are part of [`SwapParams`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) : * `**dexes**` (`string[]`) — Only route through these venues. Venues are on-chain liquidity sources — Raydium, Phoenix, Meteora, Orca, Whirlpool, PumpFun, and others. * `**excludeDexes**` (`string[]`) — Exclude these venues from routing. All other venues remain available. * `**venueAllowlist**` (`Pubkey[]`) — Constrain quotes to routes that only use venues (pools) whose **address** is in this list. Filters by individual venue address, unlike `dexes`/`excludeDexes` which filter by venue label.- `**venueBanlist**` (`Pubkey[]`) — Exclude any route that uses a venue (pool) whose address is in this list. The banlist overrides `venueAllowlist` — a venue in both lists is always excluded.- `**noVoteAccounts**` (`bool`) — Exclude a server-configured set of market-maker venues from routing. When absent or false, those venues are included as normal.- `**providers**` (`string[]`) — Only use these quote providers. Providers are the quote sources that compete to give you the best price — `Titan`, `Metis`, `Okx`, and others. * `**onlyDirectRoutes**` (`bool`) — Skip multi-hop routes. Useful when you want predictable gas costs or need to avoid complex route topologies. * `**addSizeConstraint**` (`bool`) — If true, only quotes with transactions that fit within the size constraint are returned. * `**sizeConstraint**` (`u32`) — Maximum transaction size in bytes when `addSizeConstraint` is set. Default is set by the server, normally slightly less than 1232 to allow room for additional instructions like compute budgets. * `**accountsLimitTotal**` (`u16`) — Max total accounts per route. If not set, any number that still allows an executable transaction is allowed (currently 256).- `**accountsLimitWritable**` (`u16`) — Max writable accounts per route. If not set, any number that still allows an executable transaction is allowed (currently 64). Providers and venues are independent filters. Providers decide _who computes_ the route; venues decide _where liquidity is sourced_. You can combine both. Example: combining multiple filters[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing#example-combining-multiple-filters) ------------------------------------------------------------------------------------------------------------------------------------------------------------------ Titan Direct Titan Gateway Copy import WebSocket from 'ws'; import { Encoder } from '@msgpack/msgpack'; import bs58 from 'bs58'; import { zstdCompress } from 'http-encoding'; const encoder = new Encoder({ useBigInt64: true }); const url = `${process.env.TITAN_ENDPOINT}?auth=${process.env.TITAN_API_KEY}`; const ws = new WebSocket(url, [\ 'v1.api.titan.ag+zstd',\ 'v1.api.titan.ag',\ ]); let useCompression = false; let requestId = 0; ws.on('open', async () => { useCompression = ws.protocol !== 'v1.api.titan.ag'; const encoded = encoder.encode({ id: requestId++, data: { NewSwapQuoteStream: { swap: { inputMint: bs58.decode('So11111111111111111111111111111111111111112'), outputMint: bs58.decode('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'), amount: 1_000_000_000n, slippageBps: 50, dexes: ['Raydium', 'Whirlpool', 'Phoenix'], // Only these venues providers: ['Titan', 'Metis'], // Only these providers onlyDirectRoutes: true, // No multi-hop addSizeConstraint: true, accountsLimitTotal: 40, }, transaction: { userPublicKey: bs58.decode('YOUR_WALLET_PUBLIC_KEY'), }, }, }, }); ws.send(useCompression ? await zstdCompress(encoded) : encoded); }); List available venues and providers[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing#list-available-venues-and-providers) ------------------------------------------------------------------------------------------------------------------------------------------------------------------- Query the server at runtime to discover which venues and providers are currently active. Titan Direct Titan Gateway Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing#related-pages) ----------------------------------------------------------------------------------------------------------------------- * [Stream & Execute a Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute) — full guide with transaction building and error handling * [Fee Collection](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection) — add platform fees to swap transactions * [Types Reference](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) — `SwapParams`, `TransactionParams`, and all type definitions * [GetVenues / ListProviders Reference](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/venues-providers) — complete response schemas [PreviousStream & Execute a Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute) [NextFee Collection](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection) Last updated 1 month ago * [Routing parameters](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing#routing-parameters) * [Example: combining multiple filters](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing#example-combining-multiple-filters) * [List available venues and providers](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing#list-available-venues-and-providers) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing#related-pages) Copy import { decode } from '@msgpack/msgpack'; const params = new URLSearchParams({ inputMint: 'So11111111111111111111111111111111111111112', outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', amount: '1000000000', userPublicKey: 'YOUR_WALLET_PUBLIC_KEY', slippageBps: '50', dexes: 'Raydium,Whirlpool,Phoenix', providers: 'Titan,Metis', onlyDirectRoutes: 'true', addSizeConstraint: 'true', accountsLimitTotal: '40', }); const res = await fetch( `${process.env.TITAN_ENDPOINT}/api/v1/quote/swap?${params}`, { headers: { 'Authorization': `Bearer ${process.env.TITAN_API_KEY}`, 'Accept': 'application/vnd.msgpack', }, } ); const buffer = await res.arrayBuffer(); const quotes = decode(new Uint8Array(buffer)) as any; Copy // List all venues with their program IDs sendRequest({ GetVenues: { includeProgramIds: true } }); // List all active providers sendRequest({ ListProviders: {} }); Copy import { decode } from '@msgpack/msgpack'; const venuesRes = await fetch( `${process.env.TITAN_ENDPOINT}/api/v1/venues?includeProgramIds=true`, { headers: { 'Authorization': `Bearer ${process.env.TITAN_API_KEY}`, 'Accept': 'application/vnd.msgpack', }, } ); const venues = decode(new Uint8Array(await venuesRes.arrayBuffer())) as any; console.log('Available venues:', venues.labels); const providersRes = await fetch( `${process.env.TITAN_ENDPOINT}/api/v1/providers`, { headers: { 'Authorization': `Bearer ${process.env.TITAN_API_KEY}`, 'Accept': 'application/vnd.msgpack', }, } ); const providers = decode(new Uint8Array(await providersRes.arrayBuffer())) as any[]; console.log('Active providers:', providers.map((p: any) => p.id)); --- # Onboarding (SIWS) | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding.md) . Every user makes exactly one signature before their first DCA order. `POST /partner/onboard` takes that Sign-In-with-Solana signature, provisions the user's Titan-managed manager, and records their external wallet as the funding and withdrawal address. After this, you act on the user's behalf with the `X-Titan-User` header alone — no further per-request signatures. The call is **idempotent and resumable**. Retrying with the same `sub` replays the same `userId` and `walletAddress`, so it's safe to call on every login if you'd rather not track who's already onboarded. What it provisions[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#what-it-provisions) --------------------------------------------------------------------------------------------------------------------------------- A single call resolves or creates the user's Titan identity (namespaced to your tenant), creates their **manager** with the DCA signing policy baked in, and binds their external wallet to that identity. The policy pins the manager to DCA swaps and withdrawals **only** to the user's own external wallet — so neither you nor Titan can move funds anywhere else. Build the canonical message[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#build-the-canonical-message) --------------------------------------------------------------------------------------------------------------------------------------------------- The user signs these exact bytes with their external wallet. `Address:` must equal `userPubkey`; `User:` must equal the `sub` you send in the body. Copy Titan DCA wants you to link this Solana wallet. Address: User: Issued At: Nonce: Copy function buildSiwsMessage(address: string, sub: string): string { return ( `Titan DCA wants you to link this Solana wallet.\n\n` + `Address: ${address}\n` + `User: ${sub}\n` + `Issued At: ${new Date().toISOString()}\n` + `Nonce: ${crypto.randomUUID()}\n` // trailing newline is required ); } The signed bytes must match the canonical form exactly: LF line endings, a trailing `\n` after `Nonce:`, and ≤ 1024 bytes. `Issued At` must be within ±10 minutes of server time. A mismatch returns `400 SIWS_INVALID`. The user's wallet signs the message bytes in your frontend; you forward the base58 signature to your backend: Submit it[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#submit-it) --------------------------------------------------------------------------------------------------------------- This call uses `X-Titan-Key` only — there's no `X-Titan-User` yet, because this is the call that creates the mapping. Store `userId`. It's the opaque Titan id you pass to the [partner reporting](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#partner-reporting) filters (`?userId=…`) — it is **not** your `sub`. From here on, identify the user on every call with `X-Titan-User: `. Replay and freshness[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#replay-and-freshness) ------------------------------------------------------------------------------------------------------------------------------------- Titan enforces the ±10-minute `Issued At` window plus the signature and ownership checks. The `Nonce` is opaque and not persisted, so it isn't checked for single use — a message can be re-submitted within its freshness window. That's safe because onboard is idempotent: a replay just returns the same `userId` / `walletAddress`. Generate a fresh `Issued At` and nonce per attempt anyway. Single-transaction onboarding[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#single-transaction-onboarding) ------------------------------------------------------------------------------------------------------------------------------------------------------- The flow above asks the user for two signatures before their first order: the SIWS message here, then the deposit transaction. For a brand-new wallet you can collapse that to one. Pass `onboardIfNeeded: true` on [`POST /orders/intent`](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#create-intent-then-confirm) , and if the `X-Titan-User` id was never onboarded, Titan provisions the manager inline and returns the deposit transaction as usual. That deposit is signed by `userPubkey`, so the signature doubles as the ownership proof — a wrong or unowned address can never fund the order, and until it's signed the manager is an empty wallet whose policy only permits outflows back to `userPubkey`. The flag is an explicit opt-in: it must be exactly `true`, and it only provisions **new** users. For an already-onboarded user it's ignored (the intent behaves as normal, including `403 VALIDATION_ERROR` if `userPubkey` doesn't match the attested wallet). It never links an _additional_ wallet to an existing user — that still needs the SIWS flow. **Build the two-step fallback before you ship the one-shot path.** A wallet that's brand-new to _you_ can still be known to _Titan_ — the user may have used it on the Titan app or through another partner, and wallets are recognized across the whole platform, not just your tenant. When that happens, `onboardIfNeeded: true` returns `409 USER_PUBKEY_CONFLICT` instead of silently attaching you to that account. You must detect this `409` and fall back to the two-step SIWS flow, or those users can't place their first order — and it can happen the very first time you see a user. Handling the `409`: 1 ### The one-shot intent came back `409`[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#the-one-shot-intent-came-back-409) `POST /orders/intent` with `onboardIfNeeded: true` returned `409 USER_PUBKEY_CONFLICT` — the wallet already belongs to a Titan account (yours, the Titan app's, or another partner's). 2 ### Onboard with SIWS[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#onboard-with-siws) Collect one SIWS signature and call `POST /partner/onboard` as above. This proves the user owns the wallet and links your `X-Titan-User` id to the existing account. Idempotent and safe to retry. 3 ### Re-issue the intent[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#re-issue-the-intent) Call `POST /orders/intent` again **without** `onboardIfNeeded` (the user is now onboarded), then `POST /orders/confirm` as normal. A brand-new wallet is one signature; a wallet already known to Titan is the usual two. New users — the common case — never hit the `409`. Still use `POST /partner/onboard` directly when you want to bind the wallet ahead of any deposit, or you need the `userId` for reporting before the first order. Errors[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#errors) --------------------------------------------------------------------------------------------------------- HTTP `error.code` When 400 `BAD_REQUEST` Missing `sub` / `userPubkey` / `siws.message` / `siws.signature`, or invalid JSON. 400 `SIWS_INVALID` Signature, canonical-form, freshness, or ownership check failed — bad signature, `Address` ≠ `userPubkey`, `User` ≠ `sub`, or `Issued At` outside ±10 min. 400 `PARTNER_NOT_CONFIGURED` Your tenant isn't enabled for partner onboarding. 401 `INVALID_API_KEY` / `KEY_REVOKED` / `ENV_MISMATCH` Standard `X-Titan-Key` failures. 409 `USER_PUBKEY_CONFLICT` Your `sub` and the attested wallet resolve to two different existing Titan identities. 409 `WALLET_NEEDS_USER_CONSENT` The wallet exists with no DCA setup and Titan can't attach one server-side (rare edge). 500 / 502 `PROVISIONING_FAILED` Provisioning error (`502` upstream, `500` unexpected). Safe to retry — the call is idempotent. A user-scoped call against a not-fully-provisioned manager returns `409 ONBOARDING_INCOMPLETE`. Re-call `POST /partner/onboard` (idempotent), then retry the original request. Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#related-pages) ----------------------------------------------------------------------------------------------------------------------- * [Quickstart](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart) — onboarding in the context of the full flow * [Authentication](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication) — how `X-Titan-User` resolves a user after onboarding * [Create & Manage Orders](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders) — the first thing you do once a user is onboarded [PreviousGuides](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides) [NextCreate & Manage Orders](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders) Last updated 21 days ago * [What it provisions](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#what-it-provisions) * [Build the canonical message](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#build-the-canonical-message) * [Submit it](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#submit-it) * [Replay and freshness](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#replay-and-freshness) * [Single-transaction onboarding](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#single-transaction-onboarding) * [The one-shot intent came back 409](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#the-one-shot-intent-came-back-409) * [Onboard with SIWS](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#onboard-with-siws) * [Re-issue the intent](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#re-issue-the-intent) * [Errors](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#errors) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#related-pages) Copy const message = buildSiwsMessage(userPubkey, sub); // const { signature } = await wallet.signMessage(new TextEncoder().encode(message)); // const signatureBase58 = bs58.encode(signature); Copy const res = await fetch(`${process.env.TITAN_DCA_BASE_URL}/partner/onboard`, { method: 'POST', headers: { 'X-Titan-Key': process.env.TITAN_DCA_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ sub, userPubkey, siws: { message, signature: signatureBase58 }, }), }); const { data } = await res.json(); // { userId: "", walletAddress: "" } --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/home.md). # Home !\[\](/files/aOOzIogu3HPmhifLfpAo) Titan is the swap meta-aggregator behind the best execution on Solana. Quotes from multiple providers, simulated on-chain by \*\*Argos\*\*, returned as ready-to-sign transactions. {% hint style="success" %} \*\*New: DART Swap API\*\* — Try Titan's on-chain dynamic routing for free. No API key, JSON responses, instant quotes. \[Get started →\](/titan/developer-doc/dart-swap-api/overview.md) {% endhint %} \*\*\* ## Why Titan The same infrastructure that powers titan.ag is available to you as production-grade APIs. | | | | --- | --- | | **Best Execution** | 70–75% win rate vs all other routing sources. | | **No Platform Fees** | Zero subscription or trading fees. Save ~20 bps. | | **Low Slippage** | Active simulations and algorithmic route selection. | \*\*\* ## APIs Two interfaces, same Argos routing engine. Pick the one that fits your architecture. | | | | | --- | --- | --- | | **Titan Direct** | WebSocket streaming quotes in real time. | [/pages/aLygZh4dOR3WdrWDW3aL](https://titan-exchange.gitbook.io/pages/aLygZh4dOR3WdrWDW3aL) | | **Titan Gateway** | REST endpoints, same Argos routing. | [/pages/m30FVIGPBHKIK4WAQ5bY](https://titan-exchange.gitbook.io/pages/m30FVIGPBHKIK4WAQ5bY) | | **Direct vs Gateway** | Choose the right interface for your use case. | [/pages/OwbTXLzHSPMc6ltEvYcQ](https://titan-exchange.gitbook.io/pages/OwbTXLzHSPMc6ltEvYcQ) | \*\*\* ## Get Started Everything you need to make your first request. | | | | | --- | --- | --- | | **Quickstart** | First swap quote in under 5 minutes. | [/pages/JkpBaA7trT2OpBkOEvvx](https://titan-exchange.gitbook.io/pages/JkpBaA7trT2OpBkOEvvx) | | **Authentication** | Pass your token via header or query param. | [/pages/P2gXgZIMV94wO9uzfOGK](https://titan-exchange.gitbook.io/pages/P2gXgZIMV94wO9uzfOGK) | | **Get API Access** | Get a token from Titan, Triton, or QuickNode. | [/pages/sE67p0RL6fU4fDlGnuDL](https://titan-exchange.gitbook.io/pages/sE67p0RL6fU4fDlGnuDL) | \*\*\* ## Guides Step-by-step walkthroughs for common integration tasks. | | | | | --- | --- | --- | | **Stream & Execute** | Stream quotes, build tx, send on-chain. | [/pages/b8WFvBfWgO9DwR7TvVPI](https://titan-exchange.gitbook.io/pages/b8WFvBfWgO9DwR7TvVPI) | | **Configure Routing** | Filter venues, providers, and route types. | [/pages/J6dOd3fGx4SI62x0fE2D](https://titan-exchange.gitbook.io/pages/J6dOd3fGx4SI62x0fE2D) | | **Fee Collection** | Collect platform fees with feeAccount. | [/pages/9W0HsXPfRiJIlxDpIz7m](https://titan-exchange.gitbook.io/pages/9W0HsXPfRiJIlxDpIz7m) | \*\*\* ## Resources SDKs, AI tools, and community channels. | | | | | --- | --- | --- | | **SDK Reference** | TypeScript and Rust SDKs. | [/pages/EOWiIcBdIah9TbI460vP](https://titan-exchange.gitbook.io/pages/EOWiIcBdIah9TbI460vP) | | **AI / LLM Integration** | Claude Code skill and llms.txt. | [/pages/FELSReCcxwWHyULjHZZR](https://titan-exchange.gitbook.io/pages/FELSReCcxwWHyULjHZZR) | | **Community & Support** | Discord, GitHub, and support. | [/pages/xtCDOR9C11VP4imBwPpX](https://titan-exchange.gitbook.io/pages/xtCDOR9C11VP4imBwPpX) | --- # Quote Swap | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap.md) . **Returns swap quotes with executable instructions and address lookup tables — the Gateway equivalent of** [**NewSwapQuoteStream**](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream) **.** A single REST call instead of a WebSocket stream. Copy GET /api/v1/quote/swap **Authentication** — `Authorization: Bearer ` header or `?auth=` query param. See [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#authentication) for JWT details. * * * Query parameters[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#query-parameters) ----------------------------------------------------------------------------------------------------------------------------------------- ### Required[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#required) * `**inputMint**` — Input token mint address (base58). * `**outputMint**` — Output token mint address (base58). * `**amount**` — Amount in the smallest unit (e.g. lamports for SOL). **Not scaled by decimals.** * `**userPublicKey**` — Wallet public key (base58). Required for transaction/instruction generation. ### Swap options[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#swap-options) * `**slippageBps**` — Maximum allowed slippage, in basis points. Server default applies if omitted. * `**dexes**` — Comma-separated venue labels to **include**. See [GetVenues](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#venues) for valid labels. * `**excludeDexes**` — Comma-separated venue labels to **exclude**. * `**venueAllowlist**` — Comma-separated venue **addresses** (base58) to restrict routing to. Filters by individual pool address, unlike `dexes`/`excludeDexes` which filter by venue label. * `**venueBanlist**` — Comma-separated venue **addresses** (base58) to exclude. Overrides `venueAllowlist` — an address in both lists is always excluded. * `**noVoteAccounts**` — `"true"` to exclude a server-configured set of market-maker venues from routing. These venues are included as normal when omitted or `"false"`. * `**providers**` — Comma-separated provider IDs. See [ListProviders](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info#providers) for valid IDs. * `**numQuotes**` — Maximum number of quotes to return. If more providers can quote, the worst are filtered out (by amount in/out depending on swap mode). Server default applies if omitted; validated against the server's configured bounds. On Titan Direct this is part of the streaming `update` object — on the Gateway it's a flat query param. * `**onlyDirectRoutes**` — `"true"` to skip multi-hop routes. Only direct swaps between input and output mint. * `**addSizeConstraint**` — `"true"` to only return quotes that fit within the transaction size limit. * `**sizeConstraint**` — Custom max transaction size in bytes. Default is set by the server (normally slightly less than 1232). * `**accountsLimitTotal**` — Max total accounts per route. Default: 64. * `**accountsLimitWritable**` — Max writable accounts per route. Default: 64. * `**transactionTemplate**` — A [`TransactionTemplate`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#transaction-template) encoded via **MessagePack then Base64**, passed as a query-string value. Reserves room in the transaction for instructions and ALTs you plan to prepend/append yourself, so Titan sizes routes to fit alongside them. **Incompatible with** `**accountsLimitTotal**`**,** `**accountsLimitWritable**`**, and** `**sizeConstraint**`**.** ### Transaction options[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#transaction-options) * `**feeAccount**` — Token account for collecting platform fees (base58). **Must already exist on-chain.** * `**feeBps**` — Fee amount in basis points. * `**feeFromInputMint**` — `"true"` to take fee from input mint. Default: `"false"` (fee taken from output mint). * `**closeInputTokenAccount**` — `"true"` to close the input token account as part of the transaction. * `**createOutputTokenAccount**` — `"true"` to add an idempotent ATA creation instruction. * `**outputAccount**` — Custom output token account (base58). Defaults to the user's ATA. * `**outputWsol**` — `"true"` to leave the output as **wrapped SOL** (the wSOL SPL token) instead of unwrapping it to native SOL. Default: `"false"` (output is unwrapped to native SOL). **Only has an effect when** `**outputMint**` **is wSOL** (`So11111111111111111111111111111111111111112`); ignored for any other output mint. Use it when the next step in your flow expects a wSOL token account rather than native lamports. **Requires** `**titanSwapVersion=3**`**.** * `**payer**` (base58) — Separate funder that covers the SOL-denominated costs of the swap: network fees, rent for any ATA the router creates (wSOL wrap ATA, output ATA), and the destination for the rent refund when the wSOL ATA is closed. **The payer must sign the transaction alongside the user for it to land.** **Requires** `**titanSwapVersion=3**`**.** * `**positiveSlippageFeeReceiver**` (base58) — Token account that receives any surplus when realized DEX output exceeds the quoted `outAmount`. **The skim is capped at 10 bps of** `**outAmount**` — any surplus beyond that stays with the user. **Must be a token account of the** `**outputMint**` — a wallet pubkey or wrong-mint token account fails the transaction at execution. If you want the surplus to land in a specific wallet, pass that wallet's ATA under `outputMint` (and make sure it exists before the tx runs — the router does not auto-create this account). **Requires** `**titanSwapVersion=3**`**.** ### V3 router[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#v3-router) * `**titanSwapVersion**` — `"3"` to opt into the V3 router. Titan returns V2 by default and will switch to V3 in a future release. See [NewSwapQuoteStream → Swap V3](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#swap-v3) for details. ### Performance options[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#performance-options) * `**simulate**` — `"true"` (default) or `"false"`. Simulated quotes give **more reliable execution and tighter realized slippage** because the server has verified the route against current on-chain state before returning it. **Set to** `**"false"**` **to skip simulations** — significantly reduces latency by removing an RPC round-trip, at the cost of that pre-flight check. * `**maxPriceDeviationBps**` — Max allowed deviation from reference price, in basis points. Default: `1000` (10%). **Set to** `**10000**` **or higher to disable price checking entirely.** * * * Simulation & price checking[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#simulation-and-price-checking) ----------------------------------------------------------------------------------------------------------------------------------------------------------------- By default, all quotes are **simulated before being returned** to verify they execute correctly against current on-chain state. This gives **more reliable execution and tighter realized slippage** — the server has already confirmed the route works with the latest account data. Setting `simulate=false` disables this — **significantly reducing latency** by removing an entire round of RPC calls, at the cost of that pre-flight check. To safeguard quotes without simulations, the server subscribes to **reference prices** for the requested tokens and checks that returned quotes are within a reasonable range. The `maxPriceDeviationBps` parameter controls this threshold: * **Default (**`**1000**` **/ 10%)** — Quotes must provide at least 90% of the expected value based on reference rates. Tuned to reject clearly bad quotes without being overly restrictive. * `**10000**` **or higher (≥ 100%)** — Price checking is **disabled entirely**. To avoid adding latency, the price check **fails open** — if the server doesn't have up-to-date price data for both tokens (e.g. the first time a particular token is quoted), quotes are passed through without checking. * * * Response[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#response) ------------------------------------------------------------------------------------------------------------------------- **The response body is MessagePack-encoded.** Set `Accept: application/vnd.msgpack` in your request headers. The response is a [`SwapQuotes`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#stream-updates) object — **the same type returned by Titan Direct stream updates.** It contains a `quotes` map keyed by provider ID where each value is a [`SwapRoute`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#stream-updates) with instructions and address lookup tables. `**quotes**` **is a map, not an array.** Not every provider appears in every response — iterate with `Object.entries`. ### `metadata.ExpectedWinner`[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#metadata.expectedwinner) The response includes a `metadata` object with an `ExpectedWinner` field — **this is Titan's recommendation for the best slippage-adjusted route.** Rather than sorting quotes by raw `outAmount` (which doesn't account for slippage, execution quality, or on-chain conditions), **use** `**metadata.ExpectedWinner**` **to select the route Titan expects to deliver the best actual execution.** The winner is determined by Titan's routing engine after factoring in simulation results, slippage estimates, and route reliability. * * * Example[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#example) ----------------------------------------------------------------------------------------------------------------------- * * * Error responses[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#error-responses) --------------------------------------------------------------------------------------------------------------------------------------- * `**400**` — **Invalid parameters.** Malformed pubkey, missing required field, or value out of bounds. * `**401**` — **Missing or invalid authentication token.** Check your JWT and its claims. * `**404**` — **No routes found** for this swap pair. Try relaxing routing constraints (`dexes`, `excludeDexes`, `onlyDirectRoutes`). * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#related-pages) ----------------------------------------------------------------------------------------------------------------------------------- * [NewSwapQuoteStream](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream) — Direct (WebSocket) equivalent with real-time streaming updates and full `SwapQuotes` / `SwapRoute` type definitions * [Quote Price](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price) — Lightweight price-only endpoint without transaction instructions * [Fee Collection](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection) — Collect platform fees on swaps using `feeAccount` and `feeBps` * [Configure Routing](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing) — Venue and provider filtering strategies [PreviousTitan Gateway](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway) [NextQuote Price](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-price) Last updated 1 month ago * [Query parameters](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#query-parameters) * [Required](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#required) * [Swap options](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#swap-options) * [Transaction options](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#transaction-options) * [V3 router](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#v3-router) * [Performance options](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#performance-options) * [Simulation & price checking](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#simulation-and-price-checking) * [Response](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#response) * [metadata.ExpectedWinner](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#metadata.expectedwinner) * [Example](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#example) * [Error responses](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#error-responses) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap#related-pages) Copy { "metadata": { "ExpectedWinner": "Titan-DART" }, "quotes": { ... } } Copy import { Decoder } from '@msgpack/msgpack'; // useBigInt64 required — amounts and timestamps are u64 const decoder = new Decoder({ useBigInt64: true }); // 1 SOL → USDC swap quote const params = new URLSearchParams({ inputMint: 'So11111111111111111111111111111111111111112', // SOL outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC amount: '1000000000', // 1 SOL in lamports userPublicKey: 'YOUR_WALLET_PUBLIC_KEY', slippageBps: '50', // 0.5% slippage tolerance }); // Request swap quotes from Gateway const res = await fetch( `${process.env.TITAN_ENDPOINT}/api/v1/quote/swap?${params}`, { headers: { 'Authorization': `Bearer ${process.env.TITAN_API_KEY}`, 'Accept': 'application/vnd.msgpack', // Required for MessagePack response }, } ); if (!res.ok) { throw new Error(`${res.status}: ${res.statusText}`); } // Decode the MessagePack response const buffer = await res.arrayBuffer(); const quotes = decoder.decode(new Uint8Array(buffer)) as any; // Use metadata.ExpectedWinner to pick the best slippage-adjusted route const winner = quotes.metadata?.ExpectedWinner; const route = winner && quotes.quotes[winner]; if (route?.instructions?.length) { console.log(`Best route: ${winner} — ${route.outAmount} out`); // route.instructions and route.addressLookupTables are ready for transaction building } --- # Error Codes | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes.md) . When a request fails, the server returns a `ResponseError` with a numeric `code` and a human-readable `message`. On **Titan Direct**, errors arrive as an `Error` variant of `ServerMessage`. On **Titan Gateway**, errors are returned as HTTP status codes with a MessagePack body. Error response format[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#error-response-format) ------------------------------------------------------------------------------------------------------------------------------------ Titan Direct Titan Gateway Copy { Error: { requestId: number; // Matches the ID of the original request code: number; // Numeric error code for programmatic handling message: string; // Human-readable description for logging/debugging } } The `message` field contains a specific, actionable description of the error. **Use the** `**code**` **for programmatic handling** and the `message` for logging and debugging. Status Description `400` Invalid parameters — malformed pubkey, missing required field, or invalid value. `401` Missing or invalid authentication token. `404` No routes found for this swap pair. Stream errors[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#stream-errors) -------------------------------------------------------------------------------------------------------------------- Streams can end with an error via the `StreamEnd` message: Copy { StreamEnd: { id: number; // The stream ID that has ended errorCode?: number; // Present only if the stream ended due to an error errorMessage?: string; // Human-readable reason for the error, if any } } A `StreamEnd` **without** `errorCode` indicates a clean shutdown (e.g. after `StopStream`). A `StreamEnd` **with** `errorCode` means something went wrong and you should inspect the message. * * * WebSocket close codes[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#websocket-close-codes) ------------------------------------------------------------------------------------------------------------------------------------ The server may close the WebSocket connection with a specific close code: * `**3002**` — **Protocol error.** The client sent an invalid or unsupported protocol string during negotiation, or violated the wire protocol after connecting. Reconnect with a valid `Sec-WebSocket-Protocol` header. * `**1000**` — Normal closure. The server shut down gracefully. * `**1001**` — Going away. The server is restarting or shutting down for maintenance. * * * SDK error classes[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#sdk-error-classes) ---------------------------------------------------------------------------------------------------------------------------- **If you're using the** [`**@titanexchange/sdk-ts**`](https://www.npmjs.com/package/@titanexchange/sdk-ts) **TypeScript SDK**, errors are thrown as typed classes you can catch and inspect: ### Connection errors[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#connection-errors) * `**ConnectionClosed**` — The WebSocket was closed unexpectedly. Properties: `code` (close code), `reason` (close reason string), `wasClean` (whether the close was clean). * `**ConnectionError**` — Failed to establish or maintain the WebSocket connection. Property: `cause` (underlying error). * `**InvalidProtocolError**` — The server selected an unsupported protocol string during negotiation. Property: the invalid protocol string. ### RPC errors[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#rpc-errors) * `**ErrorResponse**` — The server returned an error for a specific request. Properties: `response.code` (numeric error code), `response.message` (human-readable description), `response.requestId`. * `**StreamError**` — A stream ended with an error. Properties: `streamId`, `errorCode`, `errorMessage`. * `**ProtocolError**` — A wire-level protocol violation. Properties: `reason`, `data`. ### Codec errors[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#codec-errors) * `**DecodeError**` — Failed to decode a MessagePack message. Properties: `reason`, `value`. ### Recommended pattern[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#recommended-pattern) * * * Handling errors[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#handling-errors) ------------------------------------------------------------------------------------------------------------------------ * **Authentication errors** — Verify your token is valid, not expired, and includes the required JWT claims (`iss`, `sub`, `aud`, `exp`, `iat`). See [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection) . * **Invalid parameters** — Check that pubkeys are valid base58, amounts are positive integers, and all required fields are present. * **No routes found** — The swap pair may have insufficient liquidity, or routing constraints (`dexes`, `excludeDexes`, `onlyDirectRoutes`) may be too restrictive. **Try relaxing your filters** before assuming the pair is unsupported. * **Stream errors** — When a stream ends unexpectedly, re-open it. **Stream IDs from a previous connection are not valid after reconnect.** For reconnection patterns, see [Error Handling & Reconnect](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling) . * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#related-pages) -------------------------------------------------------------------------------------------------------------------- * [Error Handling & Reconnect](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling) — retry strategies, backoff logic, and reconnect patterns * [Wire Protocol](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol) — message envelope format and framing details * [Types Reference](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) — Types Reference — index of all type definitions [PreviousTypes Reference](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) [NextOverview](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/overview) Last updated 4 months ago * [Error response format](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#error-response-format) * [Stream errors](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#stream-errors) * [WebSocket close codes](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#websocket-close-codes) * [SDK error classes](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#sdk-error-classes) * [Connection errors](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#connection-errors) * [RPC errors](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#rpc-errors) * [Codec errors](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#codec-errors) * [Recommended pattern](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#recommended-pattern) * [Handling errors](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#handling-errors) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes#related-pages) Copy import { ErrorResponse, StreamError, ConnectionClosed } from '@titanexchange/sdk-ts'; try { // ... SDK operations } catch (err) { if (err instanceof ErrorResponse) { // Server rejected the request — check code for programmatic handling console.error(`RPC error ${err.response.code}: ${err.response.message}`); } else if (err instanceof StreamError) { // Stream ended abnormally — re-open it console.error(`Stream ${err.streamId} error: ${err.errorMessage}`); } else if (err instanceof ConnectionClosed) { // WebSocket dropped — reconnect with backoff console.warn(`Connection closed: code=${err.code}, clean=${err.wasClean}`); } } --- # SDK Reference | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk.md) . **Titan provides official SDKs for TypeScript and Rust.** Both connect to Titan Direct over WebSocket using MessagePack encoding with optional compression. * * * TypeScript SDK[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#typescript-sdk) ----------------------------------------------------------------------------------------------------- **A high-level client with built-in connection management, compression negotiation, and full type safety.** * **Package:** `@titanexchange/sdk-ts` * **Source:** [github.com/Titan-Pathfinder/titan-sdk-ts](https://github.com/Titan-Pathfinder/titan-sdk-ts) * **Node.js:** >=18.19 ### Installation[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#installation) Copy npm install @titanexchange/sdk-ts ### Connecting[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#connecting) Copy import { V1Client } from '@titanexchange/sdk-ts'; // The SDK automatically negotiates compression (zstd, brotli, gzip, or none) const url = `wss://${process.env.TITAN_ENDPOINT}/ws?auth=${process.env.TITAN_API_KEY}`; const client = await V1Client.connect(url); **Connection state:** * `**client.closed**` — `boolean`, `true` if the connection is closed. * `**client.listen_closed()**` — Returns a `Promise` that resolves with the close event. * `**client.close()**` — Gracefully closes the connection. ### API methods[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#api-methods) * `**client.getInfo()**` → `ServerInfo` — Protocol version and server settings. * `**client.newSwapQuoteStream(params)**` → `{ response, stream, streamId }` — Opens a streaming quote. * `**client.stopStream(streamId)**` → `StopStreamResponse` — Stops a stream by ID. * `**client.getVenues(params?)**` → `VenueInfo` — Lists available on-chain venues. * `**client.listProviders(params?)**` → `ProviderInfo[]` — Lists active quote providers. * `**client.getSwapPrice(params)**` → `SwapPrice` — One-shot price check without streaming. ### Streaming quotes[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#streaming-quotes) ### Stopping a stream[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#stopping-a-stream) ### Types[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#types) ### Error handling[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#error-handling) All error classes are exported from the SDK: * `**ConnectionClosed**` — WebSocket connection closed. Properties: `code`, `reason`, `wasClean`. * `**ConnectionError**` — WebSocket error event. Property: `cause`. * `**ErrorResponse**` — Server rejected a request. Property: `response` (with `code`, `message`, `requestId`). * `**StreamError**` — Stream ended with an error. Properties: `streamId`, `errorCode`, `errorMessage`. * `**ProtocolError**` — Implementation bug — **report to Titan.** Properties: `reason`, `data`. ### Reconnection[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#reconnection) **The SDK does not include built-in reconnect logic.** Handle reconnection manually: See [Error Handling & Reconnect](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling) for backoff strategies. ### Browser usage[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#browser-usage) **Do not expose your API key in client-side code.** Use a middleware proxy that accepts user connections, validates authentication, and forwards to Titan with the API key server-side. See `examples/middleware.ts` in the SDK repository. ### Key details[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#key-details) * **BigInt amounts** — Pass `amount` as `BigInt` (e.g. `1_000_000_000n`). Numbers >= 2^32 may be encoded as float64, **which the server rejects.** * `**quotes**` **is a map** — `SwapQuotes.quotes` is `Record`, keyed by provider ID. Not every provider appears in every update. * `**num_quotes**` **uses snake\_case** — In `QuoteUpdateParams`, the field is `num_quotes` (not `numQuotes`). **Logging BigInt values:** * * * Rust SDK[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#rust-sdk) ----------------------------------------------------------------------------------------- **Low-level type definitions and MessagePack codec — you manage the WebSocket connection yourself.** * **Crates:** [crates.io/search?q=titan-api-types](https://crates.io/search?q=titan-api-types) * `**titan-api-types**` — Type definitions for all WebSocket request and response messages. * `**titan-api-codec**` — MessagePack encoding/decoding with compression support (zstd, brotli, gzip). ### Installation[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#installation-1) There is **no high-level client** in the Rust SDK. You manage the WebSocket connection using `tokio-tungstenite` (or any async WebSocket library) and use the codec for serialization. ### Connecting[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#connecting-1) ### Sending requests[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#sending-requests) ### Streaming quotes[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#streaming-quotes-1) ### Processing responses[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#processing-responses) ### Field naming[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#field-naming) The Rust SDK uses standard **snake\_case** field names. Serde handles the conversion to camelCase on the wire: * `input_mint` → `inputMint` * `output_mint` → `outputMint` * `slippage_bps` → `slippageBps` * `user_public_key` → `userPublicKey` * `only_direct_routes` → `onlyDirectRoutes` ### Reconnection[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#reconnection-1) **No built-in reconnect logic.** Handle connection drops and re-establish streams manually, same as the TypeScript SDK. ### Dependencies[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#dependencies) * `**tokio-tungstenite**` — Async WebSocket client. * `**rmp-serde**` — MessagePack serialization. * `**zstd**` — Zstandard compression. * `**brotli**` — Brotli compression. * `**flate2**` — Gzip compression. * `**five8**` **/** `**five8_const**` — Base58 pubkey encoding. * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#related-pages) --------------------------------------------------------------------------------------------------- * [Quickstart](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/quickstart) — End-to-end integration example * [Stream & Execute a Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute) — Full guide with transaction building * [Wire Protocol](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol) — Encoding rules and message envelopes * [Error Codes](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes) — Error codes and SDK error classes [PreviousError Codes](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/error-codes) [NextCommunity & Support](https://titan-exchange.gitbook.io/titan/developer-doc/resources/community-and-support) Last updated 2 months ago * [TypeScript SDK](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#typescript-sdk) * [Installation](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#installation) * [Connecting](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#connecting) * [API methods](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#api-methods) * [Streaming quotes](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#streaming-quotes) * [Stopping a stream](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#stopping-a-stream) * [Types](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#types) * [Error handling](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#error-handling) * [Reconnection](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#reconnection) * [Browser usage](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#browser-usage) * [Key details](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#key-details) * [Rust SDK](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#rust-sdk) * [Installation](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#installation-1) * [Connecting](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#connecting-1) * [Sending requests](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#sending-requests) * [Streaming quotes](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#streaming-quotes-1) * [Processing responses](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#processing-responses) * [Field naming](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#field-naming) * [Reconnection](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#reconnection-1) * [Dependencies](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#dependencies) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk#related-pages) Copy import bs58 from 'bs58'; const { stream, streamId } = await client.newSwapQuoteStream({ swap: { inputMint: bs58.decode('So11111111111111111111111111111111111111112'), outputMint: bs58.decode('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'), amount: 1_000_000_000n, // 1 SOL — must be BigInt slippageBps: 50, }, transaction: { userPublicKey: bs58.decode('YOUR_WALLET_PUBLIC_KEY'), }, }); // Async iterator — yields SwapQuotes on each update for await (const quotes of stream) { // Use metadata.ExpectedWinner for the best slippage-adjusted route const winner = quotes.metadata?.ExpectedWinner; const best = winner && quotes.quotes[winner]; if (best?.instructions?.length) { console.log(`Best: ${winner} — ${best.outAmount} out`); } } Copy // Method 1: via client await client.stopStream(streamId); // Method 2: via stream — calls stopStream() internally await stream.cancel('done'); Copy import { types } from '@titanexchange/sdk-ts'; types.v1.SwapQuoteRequest; types.v1.SwapParams; types.v1.TransactionParams; Copy client.listen_closed().then(async (event) => { if (!event.wasClean) { // Reconnect and re-establish streams const newClient = await V1Client.connect(url); // Re-open your streams on newClient } }); Copy import { V1Client } from '@titanexchange/sdk-ts/browser'; Copy JSON.stringify(data, (key, value) => { if (typeof value === 'bigint') return value.toString() + 'n'; if (value instanceof Uint8Array) return ``; return value; }, 2); Copy [dependencies] titan-api-types = "5" titan-api-codec = "1.2" Copy use titan_api_codec::codec::{ws::v1::ClientCodec, Codec}; use titan_api_types::ws::v1; use tokio_tungstenite::{ connect_async, tungstenite::{ client::IntoClientRequest, http::header::{AUTHORIZATION, SEC_WEBSOCKET_PROTOCOL}, http::HeaderValue, }, }; let mut request = url.into_client_request()?; // Set protocol negotiation header let protocols = HeaderValue::from_str( &v1::WEBSOCKET_SUBPROTOCOLS.join(", ") )?; request.headers_mut().insert(SEC_WEBSOCKET_PROTOCOL, protocols); // Set auth header let bearer = HeaderValue::from_str(&format!("Bearer {}", token))?; request.headers_mut().insert(AUTHORIZATION, bearer); // Connect and create codec from negotiated protocol let (stream, response) = connect_async(request).await?; let protocol_str = response .headers() .get(SEC_WEBSOCKET_PROTOCOL) .and_then(|v| v.to_str().ok()) .unwrap(); let codec = ClientCodec::from_str(protocol_str)?; let (sink, stream) = stream.split(); Copy use titan_api_types::ws::v1::*; use titan_api_codec::codec::Codec; let encoder = codec.encoder(); let decoder = codec.decoder(); // GetInfo request let request = ClientRequest { id: 0, data: RequestData::GetInfo(GetInfoRequest::default()), }; // Encode to MessagePack and send as binary frame let bytes = encoder.encode(&request)?; sink.send(Message::Binary(bytes)).await?; Copy let request = ClientRequest { id: 1, data: RequestData::NewSwapQuoteStream(SwapQuoteRequest { swap: SwapParams { input_mint: sol_mint, output_mint: usdc_mint, amount: 1_000_000_000u64, slippage_bps: Some(50), ..Default::default() }, transaction: TransactionParams { user_public_key: wallet, ..Default::default() }, update: None, }), }; Copy while let Some(msg) = stream.next().await { let msg = msg?; if let Message::Binary(data) = msg { let server_msg: ServerMessage = decoder.decode(data.into())?; match server_msg { ServerMessage::Response(resp) => { // Handle RPC response } ServerMessage::StreamData(data) => { if let StreamDataPayload::SwapQuotes(quotes) = data.payload { for (provider, route) in "es.quotes { println!("{}: {} out", provider, route.out_amount); } } } ServerMessage::Error(err) => { eprintln!("Error {}: {}", err.code, err.message); } ServerMessage::StreamEnd(end) => { println!("Stream {} ended", end.id); } } } } --- # Quickstart | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart.md) . This walks through the full path: onboard a user once, then create a DCA order with the two-step intent/confirm flow. Everything runs from your backend with your `X-Titan-Key`. You'll need your partner **API key** and the **base URL** for your environment — both issued at onboarding. Keep the key in your backend secret store; never expose it to a browser. 1 ### Set your credentials[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart#set-your-credentials) Copy export TITAN_DCA_API_KEY="" export TITAN_DCA_BASE_URL="https://api.chronos.titan.exchange/api/v1" A small helper for every user-scoped call — note `X-Titan-User`, not a bearer token: Copy async function callTitanDca(path: string, opts: { method?: string; body?: unknown; sub: string; // your stable user id == X-Titan-User }) { const res = await fetch(`${process.env.TITAN_DCA_BASE_URL}${path}`, { method: opts.method ?? 'GET', headers: { 'X-Titan-Key': process.env.TITAN_DCA_API_KEY!, 'X-Titan-User': opts.sub, 'Content-Type': 'application/json', }, body: opts.body ? JSON.stringify(opts.body) : undefined, }); return res.json(); } 2 ### Onboard the user (once)[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart#onboard-the-user-once) Have the user sign a canonical SIWS message with their external wallet, then post it. This provisions their Titan-managed manager and records their external wallet as the funding/withdrawal address. It's idempotent — re-calling with the same inputs replays the same result. Copy const message = `Titan DCA wants you to link this Solana wallet.\n\n` + `Address: ${userPubkey}\n` + `User: ${sub}\n` + `Issued At: ${new Date().toISOString()}\n` + `Nonce: ${crypto.randomUUID()}\n`; // The user's own wallet signs `message` in your frontend; you send the signature here. const res = await fetch(`${process.env.TITAN_DCA_BASE_URL}/partner/onboard`, { method: 'POST', headers: { 'X-Titan-Key': process.env.TITAN_DCA_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ sub, userPubkey, siws: { message, signature: signatureBase58 }, }), }); const { data } = await res.json(); // Store data.userId — you'll pass it to /partners/me/* reporting filters. `User:` must equal the `sub` you'll send on every later call; `Address:` must equal the wallet pubkey. See [Onboarding](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding) for the exact message format and freshness rules. 3 ### Create a DCA order — get an unsigned deposit tx[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart#create-a-dca-order-get-an-unsigned-deposit-tx) The order's input is funded by the user's external wallet, so creation is two steps. First, `intent` returns an unsigned deposit transaction: Copy const intent = await callTitanDca('/orders/intent', { method: 'POST', sub, body: { orderType: 'dca', userPubkey, config: { inputMint: 'So11111111111111111111111111111111111111112', // SOL outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC totalAmount: '1000000000', // 1 SOL total, in lamports amountPerCycle: '100000000', // 0.1 SOL per cycle cycleFrequencySeconds: 86400, // daily }, }, }); const { pendingOrderId, transaction } = intent.data; // transaction is base64, unsigned The user signs `transaction` with their external wallet within the 5-minute window. Network fees on the recurring cycle executions are sponsored by Titan — the manager never needs SOL to keep running. 4 ### Confirm with the signed transaction[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart#confirm-with-the-signed-transaction) Copy const confirmed = await callTitanDca('/orders/confirm', { method: 'POST', sub, body: { pendingOrderId, signedTransaction, // base64, signed by the user's external wallet }, }); const { order, txSignature } = confirmed.data; Titan co-signs, submits to Solana, and activates the order. From here, Titan runs each cycle at the configured cadence — no further action from you. What success looks like[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart#what-success-looks-like) ------------------------------------------------------------------------------------------------------------------------------------ After `confirm` returns, the order is `active`: Copy { "success": true, "data": { "order": { "id": "9b3f1ad0-…", "status": "active", "orderType": "dca", "...": "…" }, "txSignature": "5Uq…", "pendingOrderId": "9b3f1ad0-…" } } Manage it[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart#manage-it) -------------------------------------------------------------------------------------------------------- Copy await callTitanDca('/me/orders/active', { sub }); // list running orders await callTitanDca(`/dca/${orderId}`, { sub }); // one order with progress await callTitanDca(`/orders/${orderId}/pause`, { method: 'POST', sub }); await callTitanDca(`/orders/${orderId}/resume`, { method: 'POST', sub }); For back-office reconciliation across your whole tenant, use the partner reporting routes with `X-Titan-Key` only — see [Endpoints → Partner reporting](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#partner-reporting) . Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart#related-pages) ---------------------------------------------------------------------------------------------------------------- * [Onboarding (SIWS)](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding) — the canonical message, freshness, and error cases * [Create & Manage Orders](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders) — the full two-step flow, modify, pause, cancel * [Authentication](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication) — headers, request tiers, auth errors [PreviousOverview](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/overview) [NextGuides](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides) Last updated 1 month ago * [Set your credentials](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart#set-your-credentials) * [Onboard the user (once)](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart#onboard-the-user-once) * [Create a DCA order — get an unsigned deposit tx](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart#create-a-dca-order-get-an-unsigned-deposit-tx) * [Confirm with the signed transaction](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart#confirm-with-the-signed-transaction) * [What success looks like](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart#what-success-looks-like) * [Manage it](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart#manage-it) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart#related-pages) --- # Fee Collection | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection.md) . If you're building a product on top of Titan, you can collect a fee on every swap. Fees are deducted from the swap output (or input) and sent to a token account you control. How fees work[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection#how-fees-work) -------------------------------------------------------------------------------------------------------------------- The fee is taken from the **output token** by default. If you'd rather take the fee from the input side, set `feeFromInputMint: true`. Your fee account must be a token account for the correct mint — output mint by default, or input mint when `feeFromInputMint` is true. This account must already exist, or you must add the ATA creation instruction yourself. When fees are active, every [`SwapRoute`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) in the quote response includes a `platformFee` field with the exact fee amount and rate. Show this to your users before they sign. These fields are part of [`TransactionParams`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) : * `**feeAccount**` (`Pubkey`) — ATA to receive the fee. Must already exist on-chain, or you must add the ATA creation instruction yourself. * `**feeBps**` (`u16`) — Fee rate in basis points (1 bps = 0.01%). If not specified, the default fee for your account is used. * `**feeFromInputMint**` (`bool`) — If `true`, fee is taken from the input mint. Default `false`. Collect fees on output token (default)[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection#collect-fees-on-output-token-default) -------------------------------------------------------------------------------------------------------------------------------------------------------------------- Create an ATA for the output mint before your first request, then pass `feeAccount` and `feeBps` in your transaction parameters. Titan Direct Titan Gateway Copy import WebSocket from 'ws'; import { Encoder, Decoder } from '@msgpack/msgpack'; import bs58 from 'bs58'; import { zstdCompress, zstdDecompress } from 'http-encoding'; const encoder = new Encoder({ useBigInt64: true }); const decoder = new Decoder({ useBigInt64: true }); const url = `${process.env.TITAN_ENDPOINT}?auth=${process.env.TITAN_API_KEY}`; const ws = new WebSocket(url, [\ 'v1.api.titan.ag+zstd',\ 'v1.api.titan.ag',\ ]); const SOL = bs58.decode('So11111111111111111111111111111111111111112'); const USDC = bs58.decode('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); const userPublicKey = bs58.decode('YOUR_WALLET_PUBLIC_KEY'); // Your ATA for the output mint (USDC in this case) const feeAccount = bs58.decode('YOUR_USDC_FEE_ATA'); let useCompression = false; let requestId = 0; ws.on('open', async () => { useCompression = ws.protocol !== 'v1.api.titan.ag'; const id = requestId++; const encoded = encoder.encode({ id, data: { NewSwapQuoteStream: { swap: { inputMint: SOL, outputMint: USDC, amount: 1_000_000_000n, // 1 SOL slippageBps: 50, }, transaction: { userPublicKey, feeAccount, feeBps: 100, // 1% fee }, }, }, }); ws.send(useCompression ? await zstdCompress(encoded) : encoded); }); Collect fees on input token[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection#collect-fees-on-input-token) ------------------------------------------------------------------------------------------------------------------------------------------------ Set `feeFromInputMint: true` and make sure your fee account is an ATA for the **input** mint instead of the output mint. Titan Direct Titan Gateway Read the fee from quote response[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection#read-the-fee-from-quote-response) ---------------------------------------------------------------------------------------------------------------------------------------------------------- Every [`SwapRoute`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) that includes a fee has a `platformFee` object. Check it before your user signs — this is what you should display in your UI. The [`PlatformFee`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) type: * `**amount**` (`u64`) — Absolute fee amount in the token's smallest unit. * `**fee_bps**` (`u8`) — Fee rate in basis points. Important notes[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection#important-notes) ------------------------------------------------------------------------------------------------------------------------ Only validated users can specify a `feeAccount`. Contact the Titan team to get your account approved for fee collection. The fee is taken **from** the swap amount, not added on top. If a user swaps 1 SOL and the fee is 1%, the user receives the output for 0.99 SOL worth of input (when `feeFromInputMint` is true) or gets 1% less output (when fees are taken from the output side, the default). Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection#related-pages) -------------------------------------------------------------------------------------------------------------------- * [Stream & Execute a Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute) — full guide with transaction building, signing, and error handling * [Configure Routing](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing) — filter venues and providers * [Types Reference](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) — `SwapRoute`, `PlatformFee`, `TransactionParams`, and all type definitions * [Wire Protocol](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol) — MessagePack encoding and compression [PreviousConfigure Routing](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing) [NextTransaction Template](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template) Last updated 4 months ago * [How fees work](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection#how-fees-work) * [Collect fees on output token (default)](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection#collect-fees-on-output-token-default) * [Collect fees on input token](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection#collect-fees-on-input-token) * [Read the fee from quote response](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection#read-the-fee-from-quote-response) * [Important notes](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection#important-notes) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection#related-pages) Copy import { decode } from '@msgpack/msgpack'; const SOL = 'So11111111111111111111111111111111111111112'; const USDC = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'; const params = new URLSearchParams({ inputMint: SOL, outputMint: USDC, amount: '1000000000', userPublicKey: 'YOUR_WALLET_PUBLIC_KEY', slippageBps: '50', feeAccount: 'YOUR_USDC_FEE_ATA', // ATA for the output mint feeBps: '100', // 1% fee }); const res = await fetch( `${process.env.TITAN_ENDPOINT}/api/v1/quote/swap?${params}`, { headers: { 'Authorization': `Bearer ${process.env.TITAN_API_KEY}`, 'Accept': 'application/vnd.msgpack', }, } ); const buffer = await res.arrayBuffer(); const quotes = decode(new Uint8Array(buffer)) as any; Copy import WebSocket from 'ws'; import { Encoder, Decoder } from '@msgpack/msgpack'; import bs58 from 'bs58'; import { zstdCompress, zstdDecompress } from 'http-encoding'; const encoder = new Encoder({ useBigInt64: true }); const decoder = new Decoder({ useBigInt64: true }); const url = `${process.env.TITAN_ENDPOINT}?auth=${process.env.TITAN_API_KEY}`; const ws = new WebSocket(url, [\ 'v1.api.titan.ag+zstd',\ 'v1.api.titan.ag',\ ]); const SOL = bs58.decode('So11111111111111111111111111111111111111112'); const USDC = bs58.decode('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); const userPublicKey = bs58.decode('YOUR_WALLET_PUBLIC_KEY'); // Fee from input — ATA must be for SOL (wrapped SOL), not USDC const feeAccount = bs58.decode('YOUR_SOL_FEE_ATA'); let useCompression = false; let requestId = 0; ws.on('open', async () => { useCompression = ws.protocol !== 'v1.api.titan.ag'; const id = requestId++; const encoded = encoder.encode({ id, data: { NewSwapQuoteStream: { swap: { inputMint: SOL, outputMint: USDC, amount: 1_000_000_000n, slippageBps: 50, }, transaction: { userPublicKey, feeAccount, feeBps: 50, // 0.5% fee feeFromInputMint: true, }, }, }, }); ws.send(useCompression ? await zstdCompress(encoded) : encoded); }); Copy import { decode } from '@msgpack/msgpack'; const SOL = 'So11111111111111111111111111111111111111112'; const USDC = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'; const params = new URLSearchParams({ inputMint: SOL, outputMint: USDC, amount: '1000000000', userPublicKey: 'YOUR_WALLET_PUBLIC_KEY', slippageBps: '50', feeAccount: 'YOUR_SOL_FEE_ATA', feeBps: '50', feeFromInputMint: 'true', }); const res = await fetch( `${process.env.TITAN_ENDPOINT}/api/v1/quote/swap?${params}`, { headers: { 'Authorization': `Bearer ${process.env.TITAN_API_KEY}`, 'Accept': 'application/vnd.msgpack', }, } ); const buffer = await res.arrayBuffer(); const quotes = decode(new Uint8Array(buffer)) as any; Copy const best = Object.values(quotes.quotes as Record) .reduce((a: any, b: any) => BigInt(b.outAmount) > BigInt(a.outAmount) ? b : a); if (best.platformFee) { const feeAmount = BigInt(best.platformFee.amount); const fee_bps = best.platformFee.fee_bps; console.log(`Platform fee: ${feeAmount} tokens (${fee_bps} bps)`); // Example: "Platform fee: 1428570 tokens (100 bps)" } // Show the fee to your user before they sign console.log(`Output after fee: ${best.outAmount}`); --- # Overview | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/overview.md) . Titan DCA lets you offer Dollar-Cost-Averaging on Solana without holding user keys, scheduling cycles, or building swap infrastructure. Your backend calls Titan; Titan provisions a managed wallet per user, runs each DCA cycle on schedule, and returns swap output to the user's own wallet. The integration is **server-to-server**. There's no SDK in your frontend and no new login UI. Your users keep authenticating however they already do; your backend authenticates to Titan with one API key and identifies each user by the same stable id you already use for them. How the pieces fit[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/overview#how-the-pieces-fit) ------------------------------------------------------------------------------------------------------------------------ Your backend holds a partner **API key** (`X-Titan-Key`) and sends it on every request. For anything user-specific, it also sends `X-Titan-User` — your own id for that user. Titan validates the key, attributes the request to your tenant, resolves the user, and executes the order on a Titan-managed Solana wallet that belongs to that user. Copy your backend ──(X-Titan-Key + X-Titan-User)──▶ Titan DCA ──▶ user's manager (policy-bound) │ └─ once per user: POST /partner/onboard (X-Titan-Key + user's SIWS) ──▶ provisions the manager The only cryptographic proof in the whole flow is a one-time **Sign-In-with-Solana (SIWS)** signature the user makes at onboarding. There's no OAuth, no token exchange, and no partner JWT. Core concepts[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/overview#core-concepts) -------------------------------------------------------------------------------------------------------------- Concept What it means for you **Tenant** Your partner account inside Titan DCA. Every order, execution, and row is tagged with your tenant id, so your data is isolated from other partners. **Partner API key** (`X-Titan-Key`) A server-side secret Titan issues you. Send it on every request. Never ship it to a browser. **Partner user id** (`X-Titan-User`) Your own stable id for an end-user. You supply it as `sub` at onboarding and as `X-Titan-User` on every user-scoped call. It's namespaced to your tenant, so two partners can use the same id without collision. **Manager** A Solana wallet created and managed by Titan but owned by the user. Titan signs only what the user's policy permits — DCA swaps, and withdrawals back to the user's own external wallet. **External wallet** (`userPubkey`) The user's existing Solana wallet. It pays network fees on user-signed transactions and, by default, receives swap output. The user proves ownership of it once, at onboarding, via SIWS. **Two-step transactions** Anything that changes on-chain state happens in two calls: **intent** (Titan returns an unsigned tx) → user signs → **confirm** (you send the signed tx back; Titan co-signs and broadcasts). **Automatic input return** When an order permanently fails, Titan returns its remaining unspent input to the user's external wallet — no partner action, no user signature. Funds reserved for the user's other active orders are left untouched. Managers are global per end-user. If the same user shows up at another integrator with the same external wallet, they land in the **same** Titan-managed manager — balances and active orders follow the user. Environments[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/overview#environments) ------------------------------------------------------------------------------------------------------------ Environment Base URL Notes Production `https://api.chronos.titan.exchange/api/v1` Issued at onboarding. A separate **development** base URL is provided at onboarding for end-to-end testing before launch. A key is bound to exactly one environment. Every endpoint path in this section is relative to the base URL. Where to go next[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/overview#where-to-go-next) -------------------------------------------------------------------------------------------------------------------- The [Quickstart](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart) takes you from onboarding a user to creating your first order. From there, the guides cover each part of an integration — [Onboarding](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding) , [Create & Manage Orders](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders) , [Withdrawals](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/withdrawals) , [Platform Fees](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/platform-fees) , and [Lifecycle & Polling](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle) . For the precise contract, see [Authentication](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication) and the full [Endpoints](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints) reference. [PreviousHow to Use](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/how-to-use) [NextQuickstart](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart) Last updated 1 month ago * [How the pieces fit](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/overview#how-the-pieces-fit) * [Core concepts](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/overview#core-concepts) * [Environments](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/overview#environments) * [Where to go next](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/overview#where-to-go-next) --- # Wire Protocol | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol.md) . This page covers how data is encoded on the wire — the serialization format, encoding conventions, and shared types used across all Titan API messages. **Both Titan Direct and Titan Gateway use MessagePack binary encoding. JSON is not supported.** Data format[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#data-format) ------------------------------------------------------------------------------------------------------------------ The basic data format for serialization of all messages is **MessagePack**. * **Objects/structs are encoded as maps** — this allows additional fields to be added without breaking compatibility with previous versions. * **Field names are** `**camelCase**` unless otherwise specified. * **Integers are encoded using the smallest MessagePack int type** that fits the value. * **Use** `**BigInt**` **for** `**u64**` **values** (amounts, timestamps) — values above 2^53 lose precision as float64, which the server rejects. Optional data[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#optional-data) ---------------------------------------------------------------------------------------------------------------------- If a value is optional, its type is `Option` in Rust and `T?` or `T | null` in TypeScript. **Optional fields in objects may be omitted entirely from the serialized map.** Otherwise, a missing optional value should be encoded as `nil` (`0xc0`) — decoded as `None` in Rust and `null` in TypeScript. Simple enumerations[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#simple-enumerations) ---------------------------------------------------------------------------------------------------------------------------------- Simple enumerations (those without associated data) are **encoded as strings matching the variant name exactly**: Rust TypeScript Copy enum SwapMode { ExactIn, ExactOut, } // Encoded as: "ExactIn" or "ExactOut" Copy enum SwapMode { ExactIn = "ExactIn", ExactOut = "ExactOut", } Complex enumerations[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#complex-enumerations) ------------------------------------------------------------------------------------------------------------------------------------ Complex enumerations (those with associated data) are **encoded as single-value maps**, mapping the variant name to the associated data. * Single associated item → the value is that data. * Multiple associated items → the value is an array. Rust TypeScript This pattern applies to both client requests (`RequestData`) and server messages (`ServerMessage`). **To determine the message type, check which key is present in the top-level map.** Binary data[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#binary-data) ------------------------------------------------------------------------------------------------------------------ Binary data is encoded using MessagePack `bin` formats. Rust TypeScript TypeScript has no way to specify byte array size — refer to the Rust types for size constraints. * * * Common types[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#common-types) -------------------------------------------------------------------------------------------------------------------- ### Pubkey[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#pubkey) **Solana public keys are 32-byte binary data.** Encoded using MessagePack `bin 8` format — all pubkeys start with `c4 20` followed by 32 bytes of key data. Example — the WSOL public key `So11111111111111111111111111111111111111112`: Rust TypeScript ### AccountMeta[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#accountmeta) Compact account descriptor used in instructions. **Uses single-letter field names to minimize message size.** Rust TypeScript ### Instruction[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#instruction) A single on-chain instruction. **Also uses single-letter field names for compactness.** Rust TypeScript `**AccountMeta**` **and** `**Instruction**` **use single-letter field names (**`**p**`**,** `**s**`**,** `**w**`**,** `**a**`**,** `**d**`**) to reduce payload size.** These are Titan's wire format, not abbreviations of the standard Solana SDK types. * * * Message envelope types[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#message-envelope-types) ---------------------------------------------------------------------------------------------------------------------------------------- ### ClientRequest[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#clientrequest) Every client request wraps an RPC method call with a monotonically increasing `id`. Rust TypeScript ### ServerMessage[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#servermessage) **The server sends one of four message types:** Rust TypeScript * * * Compression[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#compression) ------------------------------------------------------------------------------------------------------------------ Compression wraps the MessagePack payload. The order of operations: **Sending:** serialize to MessagePack → compress → send as binary WebSocket frame **Receiving:** receive binary frame → decompress → deserialize from MessagePack The compression scheme is negotiated once at connection time. See [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection) for protocol strings and setup. Gateway differences[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#gateway-differences) ---------------------------------------------------------------------------------------------------------------------------------- Titan Gateway uses the same MessagePack encoding but over HTTP REST: * **Requests** — query parameters (pubkeys as Base58 strings, not binary) * **Responses** — MessagePack body with `Content-Type: application/vnd.msgpack` * **Pubkeys in responses** are still binary `Uint8Array` in the MessagePack body * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#related-pages) ---------------------------------------------------------------------------------------------------------------------- * [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection) — WebSocket setup, authentication, and compression negotiation * [GetInfo](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info) — server settings and protocol version * [NewSwapQuoteStream](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream) — streaming swap quotes [PreviousInfo / Venues / Providers](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway/gateway-info) [NextTypes Reference](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) Last updated 4 months ago * [Data format](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#data-format) * [Optional data](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#optional-data) * [Simple enumerations](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#simple-enumerations) * [Complex enumerations](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#complex-enumerations) * [Binary data](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#binary-data) * [Common types](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#common-types) * [Pubkey](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#pubkey) * [AccountMeta](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#accountmeta) * [Instruction](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#instruction) * [Message envelope types](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#message-envelope-types) * [ClientRequest](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#clientrequest) * [ServerMessage](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#servermessage) * [Compression](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#compression) * [Gateway differences](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#gateway-differences) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol#related-pages) Copy struct Request2Data { id: u32, amount: u64, } enum Complex { Request1(String), Request2(Request2Data), Request3(u32, u32), } // Valid encodings (shown as JSON for readability): // { "Request1": "hello" } // { "Request2": {"id": 1, "amount": 34} } // { "Request3": [3, 4] } Copy interface Request2Data { id: number; amount: number; } type Complex = | { Request1: string } | { Request2: Request2Data } | { Request3: [number, number] }; Copy // Variable-sized byte array Vec // Fixed-size byte array (N bytes) [u8; N] Copy // Binary data in TypeScript Uint8Array // or ArrayBuffer Copy c4 20 069b8857feab8184fb687f634618c035dac439dc1aeb3b5598a0f00000000001 Copy // Type alias for public keys type Pubkey = [u8; 32]; Copy // Type alias for public keys to differentiate from other binary data type Pubkey = Uint8Array; // 32 bytes Copy struct AccountMeta { p: Pubkey, // public key s: bool, // is_signer w: bool, // is_writable } Copy interface AccountMeta { p: Pubkey; // public key s: boolean; // is_signer w: boolean; // is_writable } Copy struct Instruction { p: Pubkey, // program_id a: Vec, // accounts d: Vec, // data } Copy interface Instruction { p: Pubkey; // program_id a: AccountMeta[]; // accounts d: Uint8Array; // data } Copy struct ClientRequest { /// Request ID, echoed in the server's response. id: u32, /// One of the RPC method variants. data: RequestData, } enum RequestData { GetInfo(GetInfoRequest), NewSwapQuoteStream(SwapQuoteRequest), StopStream(StopStreamRequest), GetVenues(GetVenuesRequest), ListProviders(ListProvidersRequest), GetSwapPrice(SwapPriceRequest), } Copy interface ClientRequest { // Request ID, echoed in the server's response. id: number; // One of the RPC method variants. data: RequestData; } type RequestData = | { GetInfo: GetInfoRequest } | { NewSwapQuoteStream: SwapQuoteRequest } | { StopStream: StopStreamRequest } | { GetVenues: GetVenuesRequest } | { ListProviders: ListProvidersRequest } | { GetSwapPrice: SwapPriceRequest }; Copy /// A message sent by the server to the client. enum ServerMessage { /// Successful response to a request, may optionally start a stream. Response(ResponseSuccess), /// An error response to a request. Error(ResponseError), /// Data for a stream. StreamData(StreamData), /// Notification that a stream has ended. StreamEnd(StreamEnd), } /// A successful response. struct ResponseSuccess { /// Identifier of the request that triggered this response. requestId: u32, /// The response data. data: ResponseData, /// If this request starts a new stream, contains stream info. stream: Option, } /// An error response. struct ResponseError { /// Identifier of the request that triggered this response. requestId: u32, /// A numeric error code. code: u32, /// A message describing the error. message: String, } /// Data packet for a stream. struct StreamData { /// ID of the stream. id: u32, /// Sequence number of this data packet. seq: u32, /// Data payload. payload: StreamDataPayload, } /// Notification that a stream has closed. struct StreamEnd { /// ID of the stream that has ended. id: u32, /// Error code, if the stream ended abnormally. errorCode: Option, /// Error message, if the stream ended abnormally. errorMessage: Option, } /// Notification that a new stream has been started. struct StreamStart { /// Stream ID — present in all StreamData and StreamEnd for this stream. id: u32, /// Type of data that will be sent in this stream. dataType: StreamDataType, } enum StreamDataType { SwapQuotes, // May be expanded in the future. } enum StreamDataPayload { SwapQuotes(SwapQuotes), // May be expanded in the future. } enum ResponseData { GetInfo(ServerInfo), NewSwapQuoteStream(QuoteSwapStreamResponse), StreamStopped(StopStreamResponse), GetVenues(VenueInfo), ListProviders(Vec), GetSwapPrice(SwapPrice), } Copy type ServerMessage = | { Response: ResponseSuccess } | { Error: ResponseError } | { StreamData: StreamData } | { StreamEnd: StreamEnd }; interface ResponseSuccess { // Identifier of the request that triggered this response. requestId: number; // The response data. data: ResponseData; // If the request started a new stream, contains stream info. stream?: StreamStart; } interface ResponseError { // Identifier of the request that triggered this response. requestId: number; // A numeric error code. code: number; // A message describing the error. message: string; } interface StreamData { // ID of the stream. id: number; // Sequence number of this data packet. seq: number; // Data payload. payload: StreamDataPayload; } interface StreamEnd { // ID of the stream that has ended. id: number; // Error code, if the stream ended abnormally. errorCode?: number; // Error message, if the stream ended abnormally. errorMessage?: string; } interface StreamStart { // Stream ID. id: number; // Type of data that will be sent in this stream. dataType: StreamDataType; } enum StreamDataType { SwapQuotes = "SwapQuotes", } type StreamDataPayload = { SwapQuotes: SwapQuotes }; type ResponseData = | { GetInfo: ServerInfo } | { NewSwapQuoteStream: QuoteSwapStreamResponse } | { StreamStopped: StopStreamResponse } | { GetVenues: VenueInfo } | { ListProviders: ProviderInfo[] } | { GetSwapPrice: SwapPrice }; --- # Order & Execution Schema | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema.md) . The objects the DCA Partner API returns. Amounts are integer strings in the token's smallest unit; timestamps are ISO-8601 unless noted as Unix seconds. Treat all ids as opaque. Order[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema#order) ------------------------------------------------------------------------------------------------------ Returned by `GET /dca/{orderId}`, `POST /orders/confirm`, and the order lists. Copy { "id": "9b3f1ad0-7c34-4e1f-bcfb-1c9a5a3a7b21", "tenantId": "", "originatingPartner": { "id": "your-partner-id", "name": "Your Brand" }, "userId": "", "walletAddress": "GZk2v…", "outputRecipientAddress": "GZk2v…", "orderType": "dca", "status": "active", "previousStatus": null, "inputMint": "So111…", "outputMint": "EPjFW…", "totalAmount": "1000000000", "amountPerCycle": "100000000", "cycleFrequencySeconds": 86400, "minOutputPerCycle": null, "maxOutputPerCycle": null, "cyclesCompleted": 3, "totalCycles": 10, "amountSpent": "300000000", "amountReceived": "150000000", "startAt": null, "expiresAt": null, "nextExecutionAt": "2025-01-04T00:00:00.000Z", "lastExecutionAt": "2025-01-03T00:00:00.000Z", "lastExecutionTxHash": "", "createdAt": "2025-01-01T00:00:00.000Z", "updatedAt": "2025-01-01T00:00:00.000Z", "cancelledAt": null, "completedAt": null, "failedAt": null, "failureReason": null, "withdrawalStatus": "none", "withdrawalTxHash": null, "withdrawalRequestedAt": null, "withdrawalCompletedAt": null, "platformFeeBpsOverride": 50 } Field Meaning `tenantId` Your tenant uuid. Present on user-scoped reads; **omitted** on `/partners/me/*` reporting reads. `originatingPartner` Resolved partner attribution. Present on user-scoped reads; **omitted** on `/partners/me/*` reads. `userId` The opaque Titan user id (same value `GET /me` returns). `walletAddress` The user's **manager** (Titan-managed Solana pubkey). `outputRecipientAddress` Where each cycle's output lands — the manager (`walletAddress`) or the user's external `userPubkey`. Immutable after create. `status` `pending` | `active` | `executing` | `pending_modification` | `paused` | `completed` | `cancelled` | `failed` | `expired`. `previousStatus` The resting state to render while an order passes through `executing` / `pending_modification`. `null` otherwise. `cyclesCompleted` / `totalCycles` Progress counters. `amountSpent` / `amountReceived` Cumulative input spent and output received across all cycles. `nextExecutionAt` / `lastExecutionAt` `null` before the first cycle / after a terminal state. `lastExecutionTxHash` Solana signature of the most recent successful cycle, or `null`. `failureReason` Populated when `status = failed`. Short label suitable for surfacing. `withdrawalStatus` `none` | `pending` | `completed`. Independent of `status`. `withdrawalTxHash` Signature of the fund return once `withdrawalStatus = completed`, or `null` (including when a failed order's auto-return found nothing to send). Always a real signature or `null` — never a placeholder. `withdrawalRequestedAt` / `withdrawalCompletedAt` Track the withdrawal lifecycle in step with `withdrawalStatus`. `platformFeeBpsOverride` The per-order fee override, if one was set. Rely only on the fields documented here. A response may include additional fields not listed above — treat them as internal and subject to change without notice. Execution[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema#execution) -------------------------------------------------------------------------------------------------------------- Returned by `GET /orders/{orderId}/executions` and `GET /partners/me/executions`. Capped at 500 rows, most recent first. Field Notes `executionType` `dca_cycle`. `status` `pending` (in flight), `success` (confirmed on-chain), `failed` (terminal for that cycle). `price` Integer string — a fixed-point number scaled by `priceDecimals`. `price = "500000"` with `priceDecimals = 6` means 0.5 output per input. Don't parse it as a float. `outputAmountUsd` Same fixed-point convention with `outputAmountUsdDecimals`. May be `null` if pricing was unavailable at execution time. `failureReason` Short error label when `status = failed`; `null` otherwise. (The on-the-wire field is `failureReason`, not `errorMessage`.) `platformFee*` Snapshot of the fee taken on this cycle. All four populate together when a fee was charged; all four are `null` when none was (zero `bps`, or no tenant fee wallet). Balance row[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema#balance-row) ------------------------------------------------------------------------------------------------------------------ Returned by `GET /me/balance` inside `data.balances[]`. Field Meaning `totalBalance` Raw on-chain balance of the mint in the manager. `lockedForFutureTxns` Reserved by active DCA orders (`totalAmount − amountSpent` of the input mint). Output mints are not locked. `withdrawalPending` Reserved by an in-flight order-level withdrawal. `availableToWithdraw` `max(total − locked − withdrawalPending, 0)`. Source of truth for "withdraw max". `programId` One of `native`, `token`, `token-2022`. `symbol` Resolved for `SOL`, `USDC`, `USDT`; otherwise `null` (look it up in your token registry). Conventions[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema#conventions) ------------------------------------------------------------------------------------------------------------------ Amounts are integer strings in the smallest unit, to avoid JS precision loss. Order, pending-order, and execution ids are UUIDv4; the Titan `userId` is an opaque string. Both legacy SPL Token and Token-2022 mints are supported, detected per mint — though Token-2022 mints with transfer hooks, transfer fees, or unusual extensions may fail to swap or transfer, and the API surfaces an explicit error when they do. Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema#related-pages) ---------------------------------------------------------------------------------------------------------------------- * [Lifecycle & Polling](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle) — how `status` and `withdrawalStatus` transition * [Platform Fees](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/platform-fees) — how the execution fee snapshot is produced * [Endpoints](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints) — which routes return each object [PreviousEndpoints](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints) [NextError Codes](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes) Last updated 1 month ago * [Order](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema#order) * [Execution](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema#execution) * [Balance row](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema#balance-row) * [Conventions](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema#conventions) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema#related-pages) Copy { "id": "c12a8b6f-2f5a-4e26-9c1b-8a4f9e7d1b22", "orderId": "9b3f1ad0-…", "executionType": "dca_cycle", "status": "success", "inputAmount": "100000000", "outputAmount": "50000000", "outputAmountUsd": "5000000", "outputAmountUsdDecimals": 6, "price": "500000", "priceDecimals": 6, "txSignature": "", "failureReason": null, "executedAt": "2025-01-02T00:00:00.000Z", "platformFeeWallet": "FeEa…", "platformFeeBps": 50, "platformFeeMint": "EPjFW…", "platformFeeAmount": "25000" } --- # Endpoints | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints.md) . The full route contract. Paths are relative to your environment base URL. The auth tier per route is in [Authentication](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication) ; the complete error catalog is in [Error Codes](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes) . Every response uses the envelope `{ "success": true, "data": … }` on success and `{ "success": false, "error": { "code", "message", "details" } }` on failure. The one exception is `GET /health`. Public[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#public) ----------------------------------------------------------------------------------------------------------- ### `GET /health`[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#get-health) Liveness probe. No auth. Returns `200` with `status: "ok"` or `status: "degraded"`, or `503` with a flat `error` string if the check fails. This is the only endpoint that doesn't use the standard envelope. Onboarding[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#onboarding) ------------------------------------------------------------------------------------------------------------------- ### `POST /partner/onboard`[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#post-partner-onboard) `X-Titan-Key` only. Provisions a user's manager from a one-time SIWS signature. Idempotent. Full walkthrough in [Onboarding](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding) . Copy // Request { "sub": "", "userPubkey": "", "siws": { "message": "", "signature": "" } } // Response { "success": true, "data": { "userId": "", "walletAddress": "" } } User session[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#user-session) ----------------------------------------------------------------------------------------------------------------------- ### `GET /me`[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#get-me) `X-Titan-Key` + `X-Titan-User`. Resolves the current user. Returns `{ userId, walletAddress, sessionId }` (`sessionId` is always empty for partners). Balances[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#balances) --------------------------------------------------------------------------------------------------------------- ### `GET /me/balance?hideZero=false`[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#get-me-balance-hidezero-false) Per-mint view of the manager, split into total / locked / available. `hideZero=true` drops rows where all buckets are zero. Field semantics are in [Order & Execution Schema](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema#balance-row) . Errors: `401 UNAUTHORIZED`, `500 RPC_ERROR`. DCA orders — create[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#dca-orders-create) ----------------------------------------------------------------------------------------------------------------------------------- Always two steps. `intent` returns an unsigned deposit tx; `confirm` submits the signed tx and activates the order. ### `POST /orders/intent`[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#post-orders-intent) User-scoped. Optional `X-Idempotency-Key`. The full request body and field table are in [Create & Manage Orders](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#create-intent-then-confirm) . Pass `onboardIfNeeded: true` to provision a brand-new user inline and skip the separate SIWS step — see [Single-transaction onboarding](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#single-transaction-onboarding) . `feeLamports` is `"0"` when the deployment sponsors execution fees (the default for partner deployments); `"5000000"` (0.005 SOL) on non-sponsored deployments, included in the deposit tx. ### `POST /orders/confirm`[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#post-orders-confirm) After `200`, the order is `active` and Titan runs each cycle automatically. DCA orders — read[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#dca-orders-read) ------------------------------------------------------------------------------------------------------------------------------- Endpoint Returns `GET /me/orders?status={status}&type={type}` All of the user's orders. `status`: `pending` | `active` | `executing` | `pending_modification` | `paused` | `completed` | `cancelled` | `failed` | `expired`. `type`: `dca`. `GET /me/orders/active` Currently running orders. `GET /me/orders/history` Terminal orders (`completed` / `cancelled` / `failed` / `expired`). `GET /orders/pending` Orders awaiting confirmation (signed tx not yet submitted, or in flight). `GET /orders/pending/failed` Pending orders that timed out or failed before activating. `GET /orders/pending/history` All pending-order rows, any status, most recent first. **Capped at 50 rows.** `GET /dca/{orderId}` One DCA order with full progress. Full schema in [Order & Execution Schema](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema#order) . `GET /orders/{orderId}/executions` Per-cycle execution history, most recent first. Capped at 500 rows. `GET /dca/{orderId}` returns `404 NOT_FOUND` if the order doesn't exist, isn't a DCA order, or isn't this user's. DCA orders — modify, pause, resume, retry, cancel[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#dca-orders-modify-pause-resume-retry-cancel) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Endpoint What it does `PATCH /dca/{orderId}` Modify an `active`/`paused` order. Returns `requiresTransaction: false` for in-place edits, or `true` with an unsigned tx when `totalAmount` changes. `POST /dca/{orderId}/modify/confirm` Submit the signed modify tx with the matching `cyclesCompleted` + `config`. `POST /orders/{orderId}/pause` Pause an `active` order. `POST /orders/{orderId}/resume` Resume a `paused` order. `POST /orders/{orderId}/retry` Flip a `failed` order back to `active` (only before auto-return runs). `POST /orders/{orderId}/cancel` Cancel; discriminated on `withdraw` — `{ withdraw: false }` or `{ withdraw: true, userPubkey }`. The flows, gotchas, and per-endpoint error codes are in [Create & Manage Orders](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders) . Wallet-level withdrawals[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#wallet-level-withdrawals) ----------------------------------------------------------------------------------------------------------------------------------------------- Endpoint What it does `POST /withdraw/transaction` Build an unsigned wallet withdrawal tx. Body: `{ userPubkey, tokenMint, amount? }` — omit `amount` for max. `POST /withdraw/confirm` Submit the signed tx. Body: `{ signedTransaction }`. Order-level withdrawals[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#order-level-withdrawals) --------------------------------------------------------------------------------------------------------------------------------------------- Endpoint What it does `POST /orders/{orderId}/withdraw` Build a withdrawal tx for one terminal order. Returns `withdrawalAmounts` (per-mint preview). Safe to retry. `POST /orders/{orderId}/withdraw/confirm` Submit the signed tx. `POST /orders/{orderId}/withdraw/abandon` Release a stuck `withdrawalStatus: pending` lock. Idempotent. Both withdrawal flows are walked through in [Withdrawals](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/withdrawals) . Partner reporting[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#partner-reporting) --------------------------------------------------------------------------------------------------------------------------------- Server-to-server, `X-Titan-Key` only — no `X-Titan-User`. Rows are scoped to your tenant and capped at 500. Narrow to one user with `?userId=` (the opaque Titan id, not your `sub`). ### `GET /partners/me/orders`[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#get-partners-me-orders) Query params: `status` (comma-separated; allowed subset: `active`, `paused`, `completed`, `failed`, `cancelled`), `orderType` (`dca`), `userId`, `createdAtGte`, `createdAtLte` (ISO-8601 — an invalid timestamp returns `400 VALIDATION_ERROR`). The `status` filter here is a deliberate subset — `executing`, `pending`, `pending_modification`, and `expired` can't be filtered on this endpoint. Orders in those states still appear in unfiltered responses, just not when `status=` is set. Each row is the same shape as `GET /dca/{orderId}` **except** `originatingPartner` and the internal `tenantId` are omitted — every row here is yours by definition. ### `GET /partners/me/orders/{id}`[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#get-partners-me-orders-id) A single order by id, scoped to your tenant. `404` if it doesn't exist **or** belongs to another partner (no cross-tenant existence leak). ### `GET /partners/me/executions`[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#get-partners-me-executions) Per-execution rows including the fee snapshot, for billing reconciliation. Query params: `userId`, `createdAtGte`, `createdAtLte`. Row shape and fixed-point rules are in [Order & Execution Schema](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema#execution) . Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#related-pages) ------------------------------------------------------------------------------------------------------------------------- * [Order & Execution Schema](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema) — every field on an order, execution, and balance row * [Error Codes](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes) — the complete catalog, grouped by category * [Limits & Idempotency](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits) — row caps, TTLs, and the idempotency contract [PreviousAuthentication](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication) [NextOrder & Execution Schema](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema) Last updated 21 days ago * [Public](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#public) * [GET /health](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#get-health) * [Onboarding](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#onboarding) * [POST /partner/onboard](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#post-partner-onboard) * [User session](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#user-session) * [GET /me](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#get-me) * [Balances](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#balances) * [GET /me/balance?hideZero=false](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#get-me-balance-hidezero-false) * [DCA orders — create](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#dca-orders-create) * [POST /orders/intent](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#post-orders-intent) * [POST /orders/confirm](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#post-orders-confirm) * [DCA orders — read](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#dca-orders-read) * [DCA orders — modify, pause, resume, retry, cancel](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#dca-orders-modify-pause-resume-retry-cancel) * [Wallet-level withdrawals](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#wallet-level-withdrawals) * [Order-level withdrawals](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#order-level-withdrawals) * [Partner reporting](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#partner-reporting) * [GET /partners/me/orders](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#get-partners-me-orders) * [GET /partners/me/orders/{id}](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#get-partners-me-orders-id) * [GET /partners/me/executions](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#get-partners-me-executions) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints#related-pages) Copy { "success": true, "data": { "walletAddress": "GZk2v…", "balances": [\ {\ "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",\ "symbol": "USDC", "decimals": 6, "programId": "token",\ "totalBalance": "1000000", "lockedForFutureTxns": "600000",\ "withdrawalPending": "0", "availableToWithdraw": "400000",\ "lockedBreakdown": [\ { "orderId": "…", "orderType": "dca", "status": "active", "kind": "locked", "amount": "600000" }\ ]\ }\ ] } } Copy // Response { "success": true, "data": { "pendingOrderId": "9b3f1ad0-…", "memoId": "9b3f1ad0-…", "transaction": "", "encoding": "base64", "expiresAt": "2025-01-01T00:05:00.000Z", "orderType": "dca", "outputRecipientAddress": "", "inputMint": "So111…", "inputAmount": "1000000000", "feeLamports": "0" } } Copy // Request { "pendingOrderId": "9b3f1ad0-…", "signedTransaction": "" } // Response { "success": true, "data": { "order": { "…": "…" }, "txSignature": "", "pendingOrderId": "9b3f1ad0-…" } } --- # Create & Manage Orders | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders.md) . A DCA order spends a fixed `amountPerCycle` of the input mint on a schedule until `totalAmount` is exhausted. Creating one is always two steps, because the input is funded from the user's external wallet and only the user can sign that deposit. Once the order is active, Titan runs each cycle on its own. Create — intent then confirm[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#create-intent-then-confirm) ----------------------------------------------------------------------------------------------------------------------------------------------- `POST /orders/intent` returns an unsigned deposit transaction. Nothing moves until the user signs it and you confirm. Copy const intent = await callTitanDca('/orders/intent', { method: 'POST', sub, body: { orderType: 'dca', userPubkey, onboardIfNeeded: true, // optional: must be exactly true to provision a brand-new user inline platformFee: { bps: 50 }, // optional per-order override; omit for your tenant default config: { inputMint: 'So11111111111111111111111111111111111111112', outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', totalAmount: '1000000000', amountPerCycle: '100000000', cycleFrequencySeconds: 86400, minOutputPerCycle: '0', // optional: abort a cycle if quote is below this maxOutputPerCycle: '0', // optional: abort a cycle if quote is above this startAt: 1735689600, // optional: Unix seconds; first cycle waits until then expiresAt: 1767225600, // optional: Unix seconds; order auto-expires }, }, }); const { pendingOrderId, transaction, expiresAt } = intent.data; `userPubkey` must be the wallet the user attested at onboarding — anything else returns `403 VALIDATION_ERROR`. The response `transaction` is base64 and unsigned; the user has until `expiresAt` (5 minutes) to sign it. To onboard a brand-new user and create their first order in a single signature, pass `onboardIfNeeded: true` here — Titan provisions the manager inline if the `X-Titan-User` id was never onboarded. This path requires a `409 USER_PUBKEY_CONFLICT` fallback to the two-step SIWS flow. See [Single-transaction onboarding](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding#single-transaction-onboarding) . `amountPerCycle` must be worth at least **$10 USD** per cycle by default (your tenant's configured floor). USDC and USDT count as $1.00; other mints are priced by the oracle at request time. Below the floor returns `400 MIN_NOTIONAL_NOT_MET`. Then confirm with the signed transaction: Send an `X-Idempotency-Key` on `intent` and `confirm` if you intend to retry them. Use a deterministic key per logical action — e.g. `dca:create::v1` — so a network retry hashes to the same key instead of creating a second order. See [Limits & Idempotency](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits#idempotency) . ### Where swap output lands[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#where-swap-output-lands) By default each cycle's output goes to the user's external wallet (`userPubkey`). To keep it inside the manager instead — useful if the user is accumulating before a single withdrawal — set `outputRecipientAddress` to the manager address (`walletAddress` from onboarding). It must be either the `userPubkey` or the manager, and it's immutable once the order is confirmed. Read order state[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#read-order-state) ------------------------------------------------------------------------------------------------------------------------- Endpoint Returns `GET /me/orders?status=&type=` All of the user's orders, optionally filtered. `GET /me/orders/active` Currently running orders. `GET /me/orders/history` Terminal orders (`completed` / `cancelled` / `failed` / `expired`). `GET /dca/{orderId}` One order with full progress — `cyclesCompleted`, `amountSpent`, `nextExecutionAt`, and more. `GET /orders/{orderId}/executions` Per-cycle execution history, most recent first. See [Order & Execution Schema](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema) for every field. Modify[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#modify) ----------------------------------------------------------------------------------------------------- `PATCH /dca/{orderId}` edits an `active` or `paused` order. Editable fields: `amountPerCycle`, `cycleFrequencySeconds`, `totalCycles`, `totalAmount`, `minOutputPerCycle`, `maxOutputPerCycle`. Most edits apply immediately and return the updated order: Changing `totalAmount` is different: it has to deposit or withdraw the difference, so it becomes a two-step flow like creation. The response carries `requiresTransaction: true` with an unsigned `transaction`: Have the user sign it, then submit to `POST /dca/{orderId}/modify/confirm` with the same `cyclesCompleted` and `config` you got back. DCA executions for the order pause while a modification is being signed — your signed `totalAmount` can't be applied to a different on-chain state than the user reviewed. The lock auto-releases after **30 seconds**; if it expires before confirm, you get `409 LOCK_EXPIRED` and re-run the modify intent. If a cycle lands mid-signature, `cyclesCompleted` won't match and confirm returns `400 MODIFICATION_ERROR` — re-fetch and let the user re-confirm. Pause, resume, retry[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#pause-resume-retry) ------------------------------------------------------------------------------------------------------------------------------- `POST /orders/{orderId}/pause` stops an `active` order; `POST /orders/{orderId}/resume` restarts a `paused` one. Each returns the updated order, or `400 INVALID_STATE` if the order isn't in the right state or a withdrawal is in flight. `POST /orders/{orderId}/retry` flips a `failed` order back to `active`. There's a catch worth knowing: A failed order's unspent input is **auto-returned** to the user's external wallet shortly after it fails. Retry only succeeds in the brief window before that return runs. Once the return is in progress or done, retry returns `WITHDRAWAL_IN_PROGRESS` or `ALREADY_WITHDRAWN`, and the user has to create a new order. Cancel[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#cancel) ----------------------------------------------------------------------------------------------------- `POST /orders/{orderId}/cancel` takes a discriminated body on the `withdraw` flag: If you omit the body entirely, `withdraw` defaults to `true` — which still requires `userPubkey`. An empty body therefore fails with `400 VALIDATION_ERROR`. Always send one of the two shapes above explicitly. With `withdraw: true`, the response includes a `transaction` and a `withdrawalAmounts` array (per-mint amounts the tx will move). The user signs it, then you submit to `POST /orders/{orderId}/withdraw/confirm`. If there's nothing to withdraw, the response omits `transaction` and sets a `message`. Errors[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#errors) ----------------------------------------------------------------------------------------------------- The create and modify flows share most failure modes. The ones worth handling explicitly: HTTP `error.code` When 400 `VALIDATION_ERROR` Invalid body, address, or amount. `details.issues` lists each failure. 400 `INVALID_MINTS` `inputMint === outputMint`. 400 `MIN_NOTIONAL_NOT_MET` Per-cycle value below your tenant's minimum. `details` echoes `mint`, `amount`, `usdCents`, `requiredCents`. 400 `EXPIRED` / `TRANSACTION_EXPIRED` The 5-minute window to sign and confirm elapsed. Request a new intent. 400 `TRANSACTION_TAMPERED` The signed tx doesn't match the unsigned one Titan returned. 403 `VALIDATION_ERROR` `userPubkey` isn't the wallet the user attested at onboarding. 409 `ONBOARDING_INCOMPLETE` Manager not fully provisioned. Re-call `POST /partner/onboard`, then retry. 409 `LOCK_FAILED` / `LOCK_EXPIRED` Concurrent or timed-out modification. Re-run the modify intent. 422 `IDEMPOTENCY_KEY_REUSED` Same `X-Idempotency-Key` with a different body. 503 `PRICE_ORACLE_UNAVAILABLE` Couldn't price a non-stable input mint to enforce the minimum. Transient — retry shortly. The full catalog, including confirm-step and withdrawal codes, is in [Error Codes](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes) . Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#related-pages) ------------------------------------------------------------------------------------------------------------------- * [Withdrawals](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/withdrawals) — wallet-level and order-level fund returns * [Lifecycle & Polling](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle) — order statuses and how to keep your UI in sync * [Order & Execution Schema](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema) — every field on an order and an execution [PreviousOnboarding (SIWS)](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding) [NextWithdrawals](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/withdrawals) Last updated 21 days ago * [Create — intent then confirm](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#create-intent-then-confirm) * [Where swap output lands](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#where-swap-output-lands) * [Read order state](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#read-order-state) * [Modify](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#modify) * [Pause, resume, retry](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#pause-resume-retry) * [Cancel](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#cancel) * [Errors](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#errors) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders#related-pages) Copy const confirmed = await callTitanDca('/orders/confirm', { method: 'POST', sub, body: { pendingOrderId, signedTransaction }, }); const { order, txSignature } = confirmed.data; // order.status === "active" Copy { "success": true, "data": { "requiresTransaction": false, "order": { "...": "…" } } } Copy { "success": true, "data": { "requiresTransaction": true, "transactionType": "deposit", "transactionAmount": "1000000000", "newTotalAmount": "2000000000", "transaction": "", "cyclesCompleted": 3, "config": { "...": "echoed config you submitted" } } } Copy // Cancel only — no transaction built. await callTitanDca(`/orders/${orderId}/cancel`, { method: 'POST', sub, body: { withdraw: false } }); // Cancel and get an unsigned multi-token withdrawal tx back. await callTitanDca(`/orders/${orderId}/cancel`, { method: 'POST', sub, body: { withdraw: true, userPubkey }, }); --- # Error Codes | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes.md) . Every error response has the same shape: Copy { "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Human-readable summary", "details": { "…": "optional, code-specific" } } } Always branch on `error.code`. The `message` is for humans and can change without notice. HTTP codes follow the usual split: `2xx` success, `4xx` client error, `409` race/state conflict, `422` idempotency mismatch, `5xx` server error. Auth & tenancy[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes#auth-and-tenancy) ------------------------------------------------------------------------------------------------------------------------------- HTTP Code Meaning 401 `UNAUTHORIZED` On a user-scoped route: `X-Titan-User` missing, or the supplied id was never onboarded — call `POST /partner/onboard` first. 401 `INVALID_API_KEY` Missing or unknown `X-Titan-Key`. 401 `KEY_REVOKED` Your key was revoked. 401 `ENV_MISMATCH` Key issued for a different environment. 403 `PRODUCT_DISABLED` DCA product disabled on your key. 403 `PRODUCT_EXPIRED` DCA grant expired. 403 `TENANT_SUSPENDED` Tenant suspended (recoverable; contact Titan). 403 `TENANT_DELETED` Tenant deleted. 403 `TENANT_NOT_PROVISIONED` Key valid, but no tenant row yet. 422 `IDEMPOTENCY_KEY_REUSED` Same `X-Idempotency-Key`, different body. Onboarding (`POST /partner/onboard`)[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes#onboarding-post-partner-onboard) -------------------------------------------------------------------------------------------------------------------------------------------------------------------- HTTP Code Meaning 400 `BAD_REQUEST` Missing `sub` / `userPubkey` / `siws.message` / `siws.signature`, or invalid JSON. 400 `SIWS_INVALID` Signature, canonical-form, freshness, or ownership check failed. 400 `PARTNER_NOT_CONFIGURED` Tenant isn't enabled for partner onboarding. 409 `USER_PUBKEY_CONFLICT` `sub` and the attested wallet resolve to two different Titan identities. 409 `WALLET_NEEDS_USER_CONSENT` Wallet exists with no DCA setup and Titan can't attach one server-side (rare). 409 `ONBOARDING_INCOMPLETE` A user-scoped call hit a not-fully-provisioned manager. Re-call onboard (idempotent), then retry. 500 / 502 `PROVISIONING_FAILED` Provisioning error (`502` upstream, `500` unexpected). Safe to retry. Orders & transactions[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes#orders-and-transactions) --------------------------------------------------------------------------------------------------------------------------------------------- HTTP Code Where / when 400 `VALIDATION_ERROR` Most invalid bodies / query params. `details` usually has a per-field breakdown. 400 `MISSING_PARAMS` `POST /orders/confirm` — required body fields absent. 400 `INVALID_REQUEST` `POST /dca/{id}/modify/confirm` — body shape invalid. 400 `INVALID_ORDER_TYPE` `orderType` not `dca`. 400 `INVALID_MINTS` Same input/output mint. 400 `MIN_NOTIONAL_NOT_MET` Per-cycle value below your tenant's minimum ($10 USD default). `details`: `mint`, `amount`, `usdCents`, `requiredCents`. 400 `INVALID_TRANSACTION` Confirm-step tx failed structural checks. 400 `INVALID_STATUS` Pending order already consumed (e.g. previously confirmed). 400 `EXPIRED` Pending order's 5-minute window elapsed (`/orders/confirm`). 400 `TRANSACTION_EXPIRED` The unsigned transaction's 5-minute window elapsed. Request a new intent. 400 `TRANSACTION_TAMPERED` Submitted signed tx doesn't match the unsigned one Titan returned. 400 `INVALID_STATE` Atomic transition race, or unsupported order state. 400 `ORDER_NOT_FAILED` `POST /orders/{id}/retry` on a non-`failed` order. `details.status` echoes current status. 400 `INSUFFICIENT_FUNDS` Wallet now lacks input mint on retry. `details`: `mint`, `required`, `currentBalance`, `gap`. 400 `NOT_CANCELLABLE` `POST /orders/{id}/cancel` on an order that can't be cancelled in its state. 400 `MODIFICATION_ERROR` Modify rejected by a server-side guard, or `cyclesCompleted` mismatch. Re-fetch, re-run modify-intent. 409 `USER_PUBKEY_CONFLICT` `POST /orders/intent` with `onboardIfNeeded: true` — the wallet already belongs to a Titan account (yours, the Titan app's, or another partner's). Fall back to the two-step SIWS flow via `POST /partner/onboard`. 403 `FORBIDDEN` Pending order on `/orders/confirm` belongs to a different user/tenant. 404 `NOT_FOUND` Resource doesn't exist or isn't yours / this user's. 409 `EXECUTION_IN_FLIGHT` A swap attempt is still reconciling. Self-resolving — retry shortly. 409 `LOCK_FAILED` Concurrent modification on the same order at `PATCH /dca/{id}`. 409 `LOCK_EXPIRED` The 30-second modify lock elapsed before `/modify/confirm`. Re-run `PATCH /dca/{id}`. Withdrawals[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes#withdrawals) ----------------------------------------------------------------------------------------------------------------------- HTTP Code Where / when 400 `INSUFFICIENT_AVAILABLE` Wallet-level: entire balance locked or wallet empty. 400 `FUNDS_LOCKED` Wallet-level: `requested > available`. `details` includes the lock breakdown. 400 `INSUFFICIENT_BALANCE` Wallet-level: `requested > chainBalance`. 400 `NOTHING_TO_WITHDRAW` Order-level: no order-owned funds remain. 400 `ALREADY_WITHDRAWN` Order-level: a prior withdrawal already completed (including a failed order's auto-return). 409 `WITHDRAWAL_IN_PROGRESS` / `WITHDRAWAL_IN_FLIGHT` Finish or abandon the pending withdrawal first. 500 `WITHDRAWAL_BUILD_FAILED` Cancel succeeded but the withdrawal tx failed to build. The order is `cancelled`; call `POST /orders/{id}/withdraw` to retry the withdrawal leg. Server & infrastructure[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes#server-and-infrastructure) ------------------------------------------------------------------------------------------------------------------------------------------------- HTTP Code Meaning 500 `RPC_ERROR` Couldn't reach Solana (e.g. on `GET /me/balance`). 500 `SUBMIT_FAILED` / `CONFIRMATION_FAILED` / `ORDER_FINALIZE_FAILED` Failure during submit / on-chain confirm / order finalization. Safe to retry — use idempotency on submit-style calls. 500 `TX_FAILED` Submitted tx didn't confirm on Solana. 500 `INTERNAL_ERROR` Unexpected server error. 503 `RPC_UNAVAILABLE` RPC temporarily unavailable. Retry shortly. 503 `PRICE_ORACLE_UNAVAILABLE` Couldn't price a non-stable input mint to enforce the per-cycle minimum. Retry shortly. Handling strategy[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes#handling-strategy) ----------------------------------------------------------------------------------------------------------------------------------- Treat `4xx` codes as actionable by your integration — fix the request, re-onboard, or surface a message to the user. Treat `409` as a transient race: back off a few seconds and retry. Treat `5xx` and `503` as retryable server-side issues, and attach an `X-Idempotency-Key` to any submit-style POST you retry so a success that you didn't see the response for isn't re-applied. See [Limits & Idempotency](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits#idempotency) . Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes#related-pages) --------------------------------------------------------------------------------------------------------------------------- * [Authentication](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication) — the auth/tenancy codes in context * [Create & Manage Orders](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders) — per-endpoint error handling for the order flows * [Limits & Idempotency](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits) — retry windows and the idempotency contract [PreviousOrder & Execution Schema](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema) [NextLimits & Idempotency](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits) Last updated 21 days ago * [Auth & tenancy](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes#auth-and-tenancy) * [Onboarding (POST /partner/onboard)](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes#onboarding-post-partner-onboard) * [Orders & transactions](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes#orders-and-transactions) * [Withdrawals](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes#withdrawals) * [Server & infrastructure](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes#server-and-infrastructure) * [Handling strategy](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes#handling-strategy) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes#related-pages) --- # NewSwapQuoteStream | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream.md) . **Opens a stream of live swap quotes.** The server responds with a stream ID and begins pushing [`StreamData`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#message-envelope-types) messages at the configured interval until the stream is [stopped](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream) or the connection closes. Request[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#request) ------------------------------------------------------------------------------------------------------------------------- The request is a `SwapQuoteRequest` with three sub-objects: `swap` (what to quote), `transaction` (wallet context for building instructions), and `update` (optional stream tuning). Rust TypeScript Copy struct SwapQuoteRequest { /// Parameters for the swap. swap: SwapParams, /// Parameters for transaction generation. transaction: TransactionParams, /// Parameters for the stream of quote updates. update: Option, } struct SwapParams { /// Address of the input mint of the swap. inputMint: Pubkey, /// Address of the desired output token for the swap. outputMint: Pubkey, /// Raw number of tokens to swap, not scaled by decimals. amount: u64, /// Swap mode for how the amount should be interpreted. /// Either ExactIn or ExactOut, defaults to ExactIn. swapMode: Option, /// Allowed slippage in basis points. slippageBps: Option, /// If set, constrain quotes to the given set of DEXes. dexes: Option>, /// If set, exclude the following DEXes when determining routes. excludeDexes: Option>, /// If true, exclude a server-configured set of market-maker venues from /// routing. Included as normal when absent or false. noVoteAccounts: Option, /// If set to true, only direct routes between the input and output mint /// will be considered. onlyDirectRoutes: Option, /// If set to true, only quotes with transactions that fit within the size /// constraint are returned. addSizeConstraint: Option, /// The size constraint to use when `addSizeConstraint` is set. /// Default is set by the server, normally slightly less than 1232 /// to allow room for additional instructions. sizeConstraint: Option, /// If set, limit quotes to the given set of provider IDs. providers: Option>, /// If set, constrain quotes to routes that only use venues (pools) whose /// address is in this list. Filters by individual venue address, unlike /// `dexes`/`excludeDexes` which filter by venue label. venueAllowlist: Option>, /// If set, exclude any route that uses a venue (pool) whose address is in /// this list. The banlist overrides `venueAllowlist`: a venue in both lists /// is always excluded. venueBanlist: Option>, /// If set, limit total number of accounts used by routes. /// If not set, up to 64 accounts are allowed. accountsLimitTotal: Option, /// If set, limit total number of writable accounts used by routes. /// If not set, up to 64 writable accounts are allowed. accountsLimitWritable: Option, /// A template of instructions and ALTs the router must leave room for in /// the transaction. Titan generates a swap that fits alongside them within /// Solana's size limits. See "Transaction Template" below. /// INCOMPATIBLE with `accountsLimitTotal`, `accountsLimitWritable`, and /// `sizeConstraint` — passing both returns an error. transactionTemplate: Option, } struct TransactionParams { /// Public key of the user requesting the swap. /// NOTE: Setting this to a read-only system account will result in /// simulations failing and no quotes being returned. userPublicKey: Pubkey, /// If true, close the input token account as part of the transaction. closeInputTokenAccount: Option, /// If true, an idempotent ATA will be added to the transactions. createOutputTokenAccount: Option, /// Token account for the output mint used to collect fees. /// Must already exist on-chain. feeAccount: Option, /// Fee amount to take, in basis points. /// If not specified, default fee for the requester is used. feeBps: Option, /// Whether the fee should be taken in terms of the input mint. /// Default is false (fee taken from output mint). feeFromInputMint: Option, /// Token account into which to place the output of the swap. /// If not specified, the user's ATA is used. outputAccount: Option, /// If true, leave the output as wrapped SOL (the wSOL SPL token) instead of /// unwrapping it to native SOL. Only has an effect when the output mint is /// wSOL; ignored otherwise. Default: false (output unwrapped to native SOL). /// Requires `titanSwapVersion: 3`. outputWsol: Option, /// Router version to use: `3` for V3, otherwise V2. See "Swap V3" below. titanSwapVersion: Option, /// Separate funder for SOL-denominated costs of the swap — network fees, /// rent for any ATA the router creates (wSOL wrap ATA, output ATA), and /// the destination for the rent refund when the wSOL ATA is closed. /// The payer must sign the transaction alongside the user for it to land. /// Requires `titanSwapVersion: 3`. payer: Option, /// Token account that receives any surplus when realized DEX output /// exceeds the quoted `outAmount`. The skim is capped at 10 bps of /// `outAmount` — any surplus beyond that stays with the user. /// MUST be a token account of the output mint — passing a wallet pubkey /// or wrong-mint token account fails the transaction at execution. /// Requires `titanSwapVersion: 3`. positiveSlippageFeeReceiver: Option, } struct QuoteUpdateParams { /// How often the server should send updates, in milliseconds. intervalMs: Option, /// Maximum number of quotes per update. /// If more are available, the worst are filtered out. numQuotes: Option, } Response[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#response) --------------------------------------------------------------------------------------------------------------------------- A successful response includes a [`QuoteSwapStreamResponse`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) with the confirmed update interval, and a [`StreamStart`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types#message-envelope-types) with the stream ID: * `**intervalMs**` (`u64`) — The actual interval the server will use for this stream. * `**stream.id**` (`u32`) — **Use this ID to** [**stop the stream**](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream) **later.** * `**stream.dataType**` — Always `"SwapQuotes"`. Stream updates[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#stream-updates) --------------------------------------------------------------------------------------------------------------------------------------- After the stream opens, the server pushes `StreamData` messages at the confirmed interval. Each contains a `SwapQuotes` payload — **a** `**quotes**` **map keyed by provider ID** where each value is a `SwapRoute` with quote details and executable instructions. ### `metadata.ExpectedWinner`[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#metadata.expectedwinner) Each `SwapQuotes` update includes a `metadata` object with an `ExpectedWinner` field — **Titan's recommendation for the best slippage-adjusted route.** Rather than sorting by raw `outAmount`, use this to select the route that Titan expects to deliver the best actual execution after factoring in slippage, simulation results, and route reliability. Rust TypeScript `**quotes**` **is a map, not an array.** Not every provider appears in every update — iterate with `Object.entries`. Transaction Template[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#transaction-template) --------------------------------------------------------------------------------------------------------------------------------------------------- `transactionTemplate` lets you reserve room in the transaction for instructions and address lookup tables that you plan to prepend or append yourself — a custom fee transfer, an oracle update, an app-specific log, etc. Titan factors the template into route sizing so the final transaction still fits within Solana's limits. Rust TypeScript `**transactionTemplate**` **is incompatible with** `**accountsLimitTotal**`**,** `**accountsLimitWritable**`**, and** `**sizeConstraint**`**.** The template is the sizing constraint — passing both returns an error. **Wire format uses single-letter field names for space efficiency.** Each `Instruction` serializes as `{ p, a, d }` (programId / accounts / data), each `AccountMeta` as `{ p, s, w }` (pubkey / isSigner / isWritable), each `AddressLookupTableAccount` as `{ p, a }` (key / addresses). Pubkeys are raw 32-byte arrays, not Base58 strings. See the [Transaction Template guide](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template) for a worked example. Swap V3[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#swap-v3) ------------------------------------------------------------------------------------------------------------------------- Swap V3 is the newer routing version of the Titan Exchange Router (`T1TANpTeScyeqVzzgNViGDNrkQ6qHz9KrSBS4aNXvGT`). Opt in by setting `titanSwapVersion: 3` in `TransactionParams` — this unlocks the `payer`, `positiveSlippageFeeReceiver`, and `outputWsol` fields. Titan returns V2 by default and will switch to V3 in a future release. Until then, every request that needs the V3-only fields must set `titanSwapVersion: 3` explicitly. Example[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#example) ------------------------------------------------------------------------------------------------------------------------- See [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection#sending-a-request) for `sendRequest` and `decodeMessage` setup. * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#related-pages) ------------------------------------------------------------------------------------------------------------------------------------- * [StopStream](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream) — stop an active stream by its ID * [Stream & Execute a Swap](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute) — full guide with transaction building, signing, and error handling * [Configure Routing](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing) — venue and provider filtering * [Fee Collection](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection) — collect platform fees on swaps * [Types Reference](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) — `SwapQuoteRequest`, `SwapRoute`, `SwapQuotes`, and all type definitions [PreviousGetInfo](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info) [NextStopStream](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream) Last updated 1 month ago * [Request](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#request) * [Response](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#response) * [Stream updates](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#stream-updates) * [metadata.ExpectedWinner](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#metadata.expectedwinner) * [Transaction Template](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#transaction-template) * [Swap V3](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#swap-v3) * [Example](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#example) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream#related-pages) Copy interface SwapQuoteRequest { // Parameters for the swap. swap: SwapParams; // Parameters for transaction generation. transaction: TransactionParams; // Parameters for the stream of quote updates. update?: QuoteUpdateParams; } interface SwapParams { // Address of input mint for the swap. inputMint: Pubkey; // Address of output mint of the swap. outputMint: Pubkey; // Raw number of tokens to swap, not scaled by decimals. amount: number; // Whether amount is in terms of inputMint or outputMint. // Defaults to ExactIn. swapMode?: SwapMode; // Maximum allowed slippage, in basis points. slippageBps?: number; // If set, constrain quotes to the given set of DEXes. dexes?: string[]; // If set, exclude the following DEXes when determining routes. excludeDexes?: string[]; // If true, exclude a server-configured set of market-maker venues from // routing. Included as normal when absent or false. noVoteAccounts?: boolean; // If true, only direct routes will be considered. onlyDirectRoutes?: boolean; // If true, only quotes that fit within the size constraint are returned. addSizeConstraint?: boolean; // The size constraint to use when `addSizeConstraint` is set. // Default is set by the server, normally slightly less than 1232. sizeConstraint?: number; // If set, limit quotes to the given set of provider IDs. providers?: string[]; // Constrain quotes to routes that only use venues (pools) whose address is // in this list. Filters by individual venue address, unlike // `dexes`/`excludeDexes` which filter by venue label. venueAllowlist?: Pubkey[]; // Exclude any route that uses a venue (pool) whose address is in this list. // The banlist overrides `venueAllowlist`: a venue in both lists is always // excluded. venueBanlist?: Pubkey[]; // Max total accounts per route. Default: 64. accountsLimitTotal?: number; // Max writable accounts per route. Default: 64. accountsLimitWritable?: number; // A template of instructions and ALTs the router must leave room for in // the transaction. Titan generates a swap that fits alongside them within // Solana's size limits. See "Transaction Template" below. // INCOMPATIBLE with `accountsLimitTotal`, `accountsLimitWritable`, and // `sizeConstraint` — passing both returns an error. transactionTemplate?: TransactionTemplate; } interface TransactionParams { // Public key of the user requesting the swap. // NOTE: Setting this to a read-only system account will result in // simulations failing and no quotes being returned. userPublicKey: Pubkey; // If true, close the input token account as part of the transaction. closeInputTokenAccount?: boolean; // If true, an idempotent ATA creation is added to the transaction. createOutputTokenAccount?: boolean; // Token account for collecting fees. Must already exist on-chain. feeAccount?: Pubkey; // Fee amount in basis points. If not specified, default fee is used. feeBps?: number; // If true, fee is taken from input mint. Default: false (output mint). feeFromInputMint?: boolean; // Custom output token account. Defaults to the user's ATA. outputAccount?: Pubkey; // If true, leave the output as wrapped SOL (the wSOL SPL token) instead of // unwrapping it to native SOL. Only has an effect when the output mint is // wSOL; ignored otherwise. Default: false (output unwrapped to native SOL). // Requires `titanSwapVersion: 3`. outputWsol?: boolean; // Router version to use: `3` for V3, otherwise V2. See "Swap V3" below. titanSwapVersion?: number; // Separate funder for SOL-denominated costs of the swap — network fees, // rent for any ATA the router creates (wSOL wrap ATA, output ATA), and // the destination for the rent refund when the wSOL ATA is closed. // The payer must sign the transaction alongside the user for it to land. // Requires `titanSwapVersion: 3`. payer?: Pubkey; // Token account that receives any surplus when realized DEX output // exceeds the quoted `outAmount`. The skim is capped at 10 bps of // `outAmount` — any surplus beyond that stays with the user. // MUST be a token account of the output mint — passing a wallet pubkey // or wrong-mint token account fails the transaction at execution. // Requires `titanSwapVersion: 3`. positiveSlippageFeeReceiver?: Pubkey; } interface QuoteUpdateParams { // How often the server should send updates, in milliseconds. intervalMs?: number; // Maximum number of quotes per update. numQuotes?: number; } Copy struct SwapQuotes { /// Unique identifier for the quote. id: String, /// Address of the input mint for this quote. inputMint: Pubkey, /// Address of the output mint for this quote. outputMint: Pubkey, /// What swap mode was used for the quotes. swapMode: SwapMode, /// Amount used for the quotes. amount: u64, /// A mapping of a provider identifier to their quoted route. quotes: HashMap, /// Metadata including the expected winning provider (when DART is enabled). metadata: Option, } struct SwapQuotesMetadata { /// The provider Titan expects to deliver the best slippage-adjusted execution. ExpectedWinner: Option, } struct SwapRoute { /// How many input tokens go through this route. inAmount: u64, /// How many output tokens are expected. outAmount: u64, /// Slippage incurred, in basis points. slippageBps: u16, /// Platform fee information, if a fee is charged. platformFee: Option, /// Steps that comprise this route. steps: Vec, /// Instructions needed to execute the route. /// May not be provided if a full transaction is provided instead. instructions: Vec, /// Address lookup tables necessary to load. addressLookupTables: Vec, /// Context slot for the route. contextSlot: Option, /// Time taken to generate the quote in nanoseconds. timeTakenNs: Option, /// If this route expires by time, millisecond UNIX timestamp. expiresAtMs: Option, /// If this route expires by slot, last valid slot. expiresAfterSlot: Option, /// Compute units this transaction is expected to consume. computeUnits: Option, /// Recommended compute unit budget. /// Higher than computeUnits to account for on-chain fluctuations. computeUnitsSafe: Option, /// Transaction for the user to sign, if instructions are not provided. transaction: Option>, /// Provider-specific reference ID for this quote. /// Mainly provided by RFQ-based providers. reference_id: Option, } struct RoutePlanStep { /// Which AMM is being executed on at this step. ammKey: Pubkey, /// Label for the protocol (e.g. "Raydium AMM", "Phoenix"). label: String, /// Input mint for this step. inputMint: Pubkey, /// Output mint for this step. outputMint: Pubkey, /// Input tokens expected to go through this step. inAmount: u64, /// Output tokens expected from this step. outAmount: u64, /// Proportion of order flow in parts per billion. allocPpb: u32, /// Mint of the fee token, if applicable. feeMint: Option, /// Fee amount charged by the venue. feeAmount: Option, /// Context slot for the pool data. contextSlot: Option, } struct PlatformFee { /// Amount of tokens taken as a fee. amount: u64, /// Fee percentage, in basis points. fee_bps: u8, } Copy interface SwapQuotes { // Unique Quote identifier. id: string; // Address of the input mint. inputMint: Uint8Array; // Address of the output mint. outputMint: Uint8Array; // What swap mode was used. swapMode: SwapMode; // Amount used for the quotes. amount: number; // A mapping of provider identifier to their quoted route. quotes: { [key: string]: SwapRoute }; } interface SwapRoute { // Input tokens going through this route. inAmount: number; // Expected output tokens. outAmount: number; // Slippage incurred, in basis points. slippageBps: number; // Platform fee information, if charged. platformFee?: PlatformFee; // Steps that comprise this route. steps: RoutePlanStep[]; // Instructions needed to execute the route. instructions: Instruction[]; // Address lookup tables necessary to load. addressLookupTables: Pubkey[]; // Context slot for the route. contextSlot?: number; // Time taken to generate the quote in nanoseconds. timeTaken?: number; // If this route expires by time, millisecond UNIX timestamp. expiresAtMs?: number; // If this route expires by slot, last valid slot. expiresAfterSlot?: number; // Compute units this transaction is expected to consume. computeUnits?: number; // Recommended compute unit budget. computeUnitsSafe?: number; // Transaction for the user to sign, if instructions not provided. transaction?: Uint8Array; // Provider-specific reference ID for this quote. referenceId?: string; } interface RoutePlanStep { // Which AMM is being executed on at this step. ammKey: Uint8Array; // Label for the protocol (e.g. "Raydium AMM", "Phoenix"). label: string; // Input mint for this step. inputMint: Uint8Array; // Output mint for this step. outputMint: Uint8Array; // Input tokens expected to go through this step. inAmount: number; // Output tokens expected from this step. outAmount: number; // Proportion of order flow in parts per billion. allocPpb: number; // Mint of the fee token, if applicable. feeMint?: Uint8Array; // Fee amount charged by the venue. feeAmount?: number; // Context slot for the pool data. contextSlot?: number; } interface PlatformFee { // Amount of tokens taken as a fee. amount: number; // Fee percentage, in basis points. fee_bps: number; } Copy struct TransactionTemplate { /// Instructions to reserve space for. It is assumed that you have /// included the input mint and output mint account creation/deletion /// instructions when necessary. i: Vec, /// Address lookup tables used in the instructions. Provide them in the /// same order you'll use when compiling the message — Solana uses ALTs /// greedily, so their effect depends on ordering. Titan extends this /// vector with any ALTs it uses for the swap. a: Vec, /// Additional account metas to include in sizing calculations. m: Vec, } Copy interface TransactionTemplate { // Instructions to reserve space for. It is assumed that you have // included the input mint and output mint account creation/deletion // instructions when necessary. i: Instruction[]; // Address lookup tables used in the instructions. Provide them in the // same order you'll use when compiling the message — Solana uses ALTs // greedily, so their effect depends on ordering. Titan extends this // array with any ALTs it uses for the swap. a: AddressLookupTableAccount[]; // Additional account metas to include in sizing calculations. m: AccountMeta[]; } Copy { "transaction": { "userPublicKey": "72neGwRAi6QWsFQjy3PkDuYBC5GNCRwC2aUMGcrkoJuP", "titanSwapVersion": 3, "payer": "Gb4WdRjp7orviHSRz88pa3y9UkArLHR4gWWSv5HP31ZW", "positiveSlippageFeeReceiver": "LzEWGGE7aGC3XVqmMhiTZsByJBhr16dJpJoNY5RuWQ5" } } Copy import bs58 from 'bs58'; // Pubkeys are 32-byte binary — decode from Base58 before sending const SOL = bs58.decode('So11111111111111111111111111111111111111112'); const USDC = bs58.decode('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); const userPublicKey = bs58.decode('YOUR_WALLET_PUBLIC_KEY'); // Open a swap quote stream — server will push updates at the configured interval await sendRequest(ws, requestId++, { NewSwapQuoteStream: { swap: { inputMint: SOL, outputMint: USDC, amount: 1_000_000_000n, // 1 SOL in lamports — must be BigInt slippageBps: 50, // 0.5% slippage tolerance }, transaction: { userPublicKey, // Needed for transaction/instruction generation }, }, }); // Track the stream ID so we can stop it later let streamId: number | undefined; ws.on('message', async (raw: Buffer) => { const msg = await decodeMessage(raw); // Stream opened — capture the stream ID and confirmed interval if ('Response' in msg && 'NewSwapQuoteStream' in msg.Response.data) { streamId = msg.Response.stream.id; console.log(`Stream ${streamId} open — interval: ${msg.Response.data.NewSwapQuoteStream.intervalMs}ms`); } // Quote update — quotes is a map keyed by provider ID, not an array if ('StreamData' in msg) { const quotes = msg.StreamData.payload.SwapQuotes; for (const [provider, route] of Object.entries(quotes.quotes as Record)) { if (route.instructions?.length) { console.log(`${provider}: ${route.outAmount} out`); } } } // RPC error — check code and message for details if ('Error' in msg) { console.error(`Error ${msg.Error.code}: ${msg.Error.message}`); } // Stream closed — check errorCode/errorMessage if present if ('StreamEnd' in msg) { console.log(`Stream ${msg.StreamEnd.id} ended`); ws.close(); } }); --- # Stream & Execute a Swap | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute.md) . This guide covers **Titan Direct** — the WebSocket path. For a single-request flow using Titan Gateway, see the [Quickstart](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/quickstart) . It walks through the complete lifecycle — connecting over WebSocket, streaming live quotes, picking the best one, building and signing a transaction, then shutting down cleanly. This guide uses raw WebSocket and MessagePack directly. If you prefer a higher-level interface, see the [`@titanexchange/sdk-ts`](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk) SDK. You need an API token and endpoint URL before starting. See [Get API Access](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/api-access) if you don't have one yet. Prerequisites[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute#prerequisites) ------------------------------------------------------------------------------------------------------------------------ Copy npm install ws @msgpack/msgpack bs58 @solana/web3.js http-encoding Set your environment variables: Copy export TITAN_ENDPOINT="wss://YOUR_ENDPOINT/api/v1/ws" export TITAN_API_KEY="YOUR_API_TOKEN" export SOLANA_RPC_URL="https://YOUR_RPC_ENDPOINT" * * * 1 **Connect and negotiate the protocol** Titan Direct uses [MessagePack](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol) over WebSocket. List your supported compression schemes in the [`Sec-WebSocket-Protocol`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection) header — the server selects the best match and confirms it on open. Copy import WebSocket from 'ws'; import { Encoder, Decoder } from '@msgpack/msgpack'; import bs58 from 'bs58'; import { zstdCompress, zstdDecompress, brotliCompress, brotliDecompress, gzipCompress, gzipDecompress, } from 'http-encoding'; // useBigInt64 ensures amounts encode as int64, not float64 const encoder = new Encoder({ useBigInt64: true }); const decoder = new Decoder({ useBigInt64: true }); // Build the WebSocket URL with auth token as query param const url = `${process.env.TITAN_ENDPOINT}?auth=${process.env.TITAN_API_KEY}`; // List supported protocols in preference order — server picks the best match // zstd gives the best compression ratio, fallback to brotli, gzip, or none const ws = new WebSocket(url, [\ 'v1.api.titan.ag+zstd', // preferred — best ratio + speed\ 'v1.api.titan.ag+brotli', // fallback\ 'v1.api.titan.ag+gzip', // fallback\ 'v1.api.titan.ag', // no compression\ ]); // Compress/decompress default to identity (no-op) — overwritten on open let compress: (data: Uint8Array) => Promise | Uint8Array = (d) => d; let decompress: (data: Uint8Array) => Promise | Uint8Array = (d) => d; let requestId = 0; ws.on('open', () => { // The server confirms which protocol it selected via ws.protocol const proto = ws.protocol; console.log('Connected — protocol:', proto); // Match the negotiated protocol to the correct codec if (proto.endsWith('+zstd')) { compress = zstdCompress; decompress = zstdDecompress; } else if (proto.endsWith('+brotli')) { compress = brotliCompress; decompress = brotliDecompress; } else if (proto.endsWith('+gzip')) { compress = gzipCompress; decompress = gzipDecompress; } // If none matched, no compression — identity functions stay in place }); // Encode a request as MessagePack, compress, and send async function sendRequest(data: Record): Promise { const id = requestId++; const encoded = encoder.encode({ id, data }); ws.send(await compress(encoded)); return id; } // Decompress an incoming binary frame and decode from MessagePack async function decodeMessage(raw: Buffer): Promise { const data = await decompress(raw); return decoder.decode(data); } ws.on('error', (err) => { console.error('WebSocket error:', err.message); }); 2 **Call GetInfo** Send [`GetInfo`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/get-info) right after connecting to confirm the connection and read the server's current defaults — update interval, slippage bounds, and stream limits. Copy ws.on('open', () => { sendRequest({ GetInfo: {} }); }); ws.on('message', async (raw: Buffer) => { const msg = await decodeMessage(raw); if ('Response' in msg && 'GetInfo' in msg.Response.data) { const info = msg.Response.data.GetInfo; console.log('Protocol version:', info.protocolVersion); console.log('Default update interval:', info.settings.quoteUpdate.intervalMs.default, 'ms'); } }); 3 **Open a quote stream** Send [`NewSwapQuoteStream`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream) to start receiving live quotes. The `swap` object defines what to quote, `transaction` provides the wallet context needed to build executable instructions. Copy const SOL = bs58.decode('So11111111111111111111111111111111111111112'); const USDC = bs58.decode('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); const userPublicKey = bs58.decode('YOUR_WALLET_PUBLIC_KEY'); sendRequest({ NewSwapQuoteStream: { swap: { inputMint: SOL, outputMint: USDC, amount: 1_000_000_000n, // 1 SOL in lamports slippageBps: 50, }, transaction: { userPublicKey, }, }, }); The server responds with a `stream.id` — save it to stop the stream later. Use `BigInt` for `amount`. Numbers above 2^32 encode as float64 in MessagePack, which the server rejects. 4 **Read quotes and pick the best** The server pushes [`StreamData`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) messages at the negotiated interval. Each update includes `metadata.ExpectedWinner` — **Titan's recommendation for the best slippage-adjusted route.** Copy let streamId: number | undefined; ws.on('message', async (raw: Buffer) => { const msg = await decodeMessage(raw); if ('Response' in msg && 'NewSwapQuoteStream' in msg.Response.data) { streamId = msg.Response.stream.id; console.log(`Stream ${streamId} open`); return; } if ('StreamData' in msg) { const swapQuotes = msg.StreamData.payload.SwapQuotes; // Use metadata.ExpectedWinner for the best slippage-adjusted route const winner = swapQuotes.metadata?.ExpectedWinner; const bestRoute = winner && swapQuotes.quotes[winner]; if (bestRoute?.instructions?.length) { console.log(`Best: ${winner} — ${bestRoute.outAmount} out`); } } }); 5 **Build, sign, and send** Each quote returns `instructions` and `addressLookupTables` as part of the [`SwapRoute`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) type. Fetch the ALT accounts, compile a V0 message, sign, and send. Use `computeUnitsSafe` for your compute budget — it accounts for on-chain variance and gives your transaction room to land without failing on a slightly heavier slot. Copy import { Connection, VersionedTransaction, TransactionMessage, AddressLookupTableAccount, TransactionInstruction, PublicKey, } from '@solana/web3.js'; const connection = new Connection(process.env.SOLANA_RPC_URL!); function toSolanaInstruction(ix: any): TransactionInstruction { return new TransactionInstruction({ programId: new PublicKey(ix.p), keys: ix.a.map((acc: any) => ({ pubkey: new PublicKey(acc.p), isSigner: acc.s, isWritable: acc.w, })), data: Buffer.from(ix.d), }); } async function executeQuote(route: any): Promise { const altAccounts: AddressLookupTableAccount[] = []; for (const altPubkey of (route.addressLookupTables ?? [])) { const { value } = await connection.getAddressLookupTable(new PublicKey(altPubkey)); if (value) altAccounts.push(value); } const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash(); const message = new TransactionMessage({ payerKey: new PublicKey(userPublicKey), recentBlockhash: blockhash, instructions: route.instructions.map(toSolanaInstruction), }).compileToV0Message(altAccounts); const tx = new VersionedTransaction(message); // Sign the transaction — use your wallet adapter or Keypair // Client-side: const signed = await signTransaction(tx); // Server-side: tx.sign([keypair]); try { const sig = await connection.sendRawTransaction(tx.serialize()); await connection.confirmTransaction({ signature: sig, blockhash, lastValidBlockHeight }); console.log('Confirmed:', sig); if (streamId !== undefined) { sendRequest({ StopStream: { id: streamId } }); } } catch (err: any) { console.error('Send failed:', err.message); } } If a route expires, `expiresAtMs` contains the expiry as a millisecond UNIX timestamp and `expiresAfterSlot` contains the last slot at which the route is valid. If present, check these before executing — a route may no longer be valid by the time your transaction lands on-chain. 6 **Stop the stream** Once you've executed, send [`StopStream`](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/stop-stream) to free up the connection for other streams. Copy sendRequest({ StopStream: { id: streamId! } }); 7 **Handle StreamEnd and errors** Copy ws.on('message', async (raw: Buffer) => { const msg = await decodeMessage(raw); if ('Error' in msg) { console.error(`Request ${msg.Error.requestId} failed [${msg.Error.code}]: ${msg.Error.message}`); } if ('StreamEnd' in msg) { const { id, errorCode, errorMessage } = msg.StreamEnd; if (errorCode) { console.error(`Stream ${id} error ${errorCode}: ${errorMessage}`); } else { console.log(`Stream ${id} closed`); } ws.close(); } }); For reconnect patterns see [Error Handling & Reconnect](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling) . * * * Key details[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute#key-details) -------------------------------------------------------------------------------------------------------------------- * `**quotes**` **shape** — `Record` keyed by provider ID, not an array. * **Transaction building** — Build a V0 transaction from `instructions` + `addressLookupTables`. * **Compute budget** — Use `computeUnitsSafe` — accounts for on-chain variance. * **Quote expiry** — If a route expires, `expiresAtMs` and `expiresAfterSlot` will be set. Check these before executing. * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute#related-pages) ------------------------------------------------------------------------------------------------------------------------ * [Configure Routing](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing) — filter venues and providers * [Error Handling & Reconnect](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling) — error codes and reconnect patterns * [Wire Protocol](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol) — MessagePack encoding and compression * [Connection & Negotiation](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection) — protocol negotiation details * [Types Reference](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types) — `SwapRoute`, `StreamData`, and all type definitions * [NewSwapQuoteStream Reference](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream) * [CPI Example (Anchor)](https://github.com/Titan-Pathfinder/titan-v2-cpi-example) — Example Anchor program demonstrating cross-program invocation into Titan's `swap_route_v2` instruction [PreviousSwap V2 vs Swap V3](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3) [NextConfigure Routing](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing) Last updated 3 months ago * [Prerequisites](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute#prerequisites) * [Key details](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute#key-details) * [Related pages](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute#related-pages) --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/getting-started/usernames-and-referrals.md). # Usernames and Referrals Titan will also allow you set your username that can be used to represent your profile. This username will also be used to refer other users to access the platform by giving them invite codes. Users who refer others will be able to get a view of the total aggregate volume and outperformance from themselves as well as their referred users. Referral invite codes can be found in the wallet panel at the top right of the panel
There may be additional benefits for both the referred and the referrer in the future. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/dex-aggregators.md). # DEX Aggregators There are multiple ways to do spot trades on Solana. You can either trade directly with a centralized exchange (CEX) such as Binance or Coinbase, a decentralized exchange (DEX) such as Orca or Raydium, or a DEX Aggregator like Titan or Jupiter. If you trade through a CEX/DEX, you are only sourcing liquidity for your trade from one venue, which may not offer the best price.
In traditional finance markets, brokers are connected to multiple sources of liquidity to source you the best price if you have an account. In crypto, the only way to do this without dedicated infrastructure and multiple compliance checks is to aggregate the liquidity among DEXes. Platforms that offer these are called DEX Aggregators. The problem that DEX Aggregators face are fairly unique. In traditional markets, the speed is so fast (nanoseconds) that order flow must be processed almost instantly, but in crypto markets, there is enough distributed liquidity and enough time to deploy advanced analytics to find the best possible route. DEX Aggregation on Solana is especially strong due to the network's low fees. This makes it feasible to find complex routes that provide better prices without it being absorbed by extremely high gas prices as in Ethereum. Due to this, DEX Aggregators are the preferred way for users to trade on low cost chains in order to maximize their asset's value. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/how-swaps-work.md). # How Swaps Work A swap is how users on a blockchain trade one token for another. A straight token to token trade without the use of leverage is called a spot swap/trade as the underlying token is the one being traded. In DeFi, a user signs a transaction through their wallet for their trade to execute on the desired platform. This transaction propagates throughout the entire blockchain worldwide and after a short delay, the trade is confirmed and you have the results of your trade in your wallet. An advantage here is that the funds are transferred immediately without waiting for the lag time that is present in traditional markets. In addition, Solana does this at very low costs (sub cent), thus making it more efficient than many money transmitting networks, especially internationally. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/editor.md). # Titan's Unique Algorithm Historically, DEX aggregators have relied on shortest path algorithms to determine routes between liquidity. In doing so, they have benefited from battle tested algorithms that are simple to implement. However, given the nature of the crypto markets, latency requirements and underlying assumptions of shortest path algorithms, liquidity sources are often temporarily removed when these types of algorithms are used in order to make routing possible. In addition to this, a specific route in a network may not have significant capacity. If the pools involved have a small TVL then the price can change rapidly as more of the users’ funds are swapped through each relevant exchange. To capture the effects of price impact, the liquidity is frequently fragmented into pieces and each pool is broken into many parts with an average price and a certain capacity. This is frequently reflected both in the algorithm and its outputs. For example, currently Jupiter, another DEX aggregator, generally chunks their routes into neat 1% buckets. This fragmentation of liquidity increases the size of the network being searched and leads to inaccuracies when the pool is poorly resolved. These two challenges are among the main obstacles in applying shortest path methods for DEX aggregation. To address them, Titan leverages a different set of algorithms that focus on optimization, which resulted in the development of the Argos algorithm. These algorithms can efficiently resolve price impacts with machine-level precision without fragmenting pools and eliminates the need to exclude various liquidity sources, thus leading to a true optimal on-chain price. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/authentication.md). # Authentication The Titan API uses \*\*JSON Web Tokens (JWTs)\*\* for authentication. Every connection requires a valid token issued by Titan or a Titan distributor. ## Submitting your token You can submit your JWT in two ways: As a \*\*Bearer token\*\* in the \`Authorization\` header — the recommended approach for server-side integrations: \`\`\` Authorization: Bearer \`\`\` As a query parameter — for browser clients or environments where setting custom headers isn't possible (e.g. WebSocket clients that only support \[\`Sec-WebSocket-Protocol\`\](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-WebSocket-Protocol)): \`\`\` wss://YOUR\_ENDPOINT/api/v1/ws?auth= https://YOUR\_ENDPOINT/api/v1/quote/swap?auth= \`\`\` {% hint style="warning" %} \*\*Do not expose your JWT in client-side code.\*\* Set up a middleware proxy that injects the token server-side. See the \[middleware example\](https://github.com/Titan-Pathfinder/titan-sdk-ts/blob/main/examples/middleware.ts) for a basic reference — you can build your own middleware to fit your stack and authentication flow. {% endhint %} {% hint style="info" %} If your connection drops unexpectedly, check your token's expiration first — an \*\*expired token is the most common cause\*\* of authentication failures. {% endhint %} ## Getting a token See \[Get API Access\](/titan/developer-doc/getting-started/api-access.md). --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/introduction.md). # Introduction Titan is a meta-aggregator for Solana. It collects quotes from multiple providers — DEX aggregators and RFQ providers — routes through Argos, and returns the best quotes. ## What Titan offers \*\*Titan Direct\*\* is a WebSocket API that streams live swap quotes, updated continuously as on-chain state changes — \*\*the only WebSocket-native trading API on Solana, built for traders who can't afford stale quotes.\*\* Use it when you need real-time pricing — trading bots, live swap UIs, or any integration where quotes should refresh automatically. \*\*Titan Gateway\*\* is a REST API that returns quotes per request — no persistent connection required. \*\*The fastest path from zero to production-grade swap execution on Solana.\*\* Use it when a single quote per action is enough — one-click swap buttons, price displays, or backend services that request quotes on demand. Both paths deliver the same execution quality through Argos. The difference is interface and workflow — not routing quality. \*\*Limit Orders\*\* lets you place resting on-chain orders that fill fully or partially as liquidity becomes available. The program supports five time-in-force modes and charges fees to takers in the output token. ## Routing with Argos Every swap request runs through \*\*Argos\*\*, Titan's proprietary routing algorithm. Where competing aggregators rely on shortest-path methods and chunk liquidity into percentage buckets, Argos resolves price impacts with machine-level precision — without fragmenting pools or excluding liquidity sources. The result is a true optimal on-chain price, which is why Titan beats competing aggregators 80% of the time. ## Where to start? Need a token first? Go to \[Get API Access\](/titan/developer-doc/getting-started/api-access.md). Ready to build? The \[Quickstart\](/titan/developer-doc/swap-api/quickstart.md) gets you your first swap quote in minutes using both Titan Direct and Titan Gateway. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/getting-started/api-access.md). # Get API Access Titan API access is available through infrastructure providers. ## Through a distributor Titan is available through the following infrastructure providers: \* \[\*\*Triton\*\*\](https://docs.triton.one/trading-apis/titan-swap-api) — Titan Swap API documentation on Triton \* \[\*\*QuickNode\*\*\](https://marketplace.quicknode.com/add-on/titan-swap) — Titan Swap add-on on the QuickNode Marketplace If you're already on either platform, you can get access without contacting the Titan team directly. {% hint style="info" %} Once you have a token and endpoint, go to the \[Quickstart\](/titan/developer-doc/swap-api/quickstart.md) to make your first request. {% endhint %} --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/quickstart.md). # Quickstart {% hint style="info" %} You'll need an API token and endpoint URL before starting. See \[Get API Access\](/titan/developer-doc/getting-started/api-access.md) if you don't have one yet. {% endhint %} This is a minimal example to get your first quote from the Titan API. For a complete integration with transaction building, signing, and error handling, see \[Stream & Execute a Swap\](/titan/developer-doc/swap-api/guides/stream-and-execute.md). Titan has two integration paths. \*\*Titan Direct\*\* uses WebSocket and streams live quotes continuously. \*\*Titan Gateway\*\* uses REST and returns a single set of quotes per request. Both deliver the same quote quality. {% hint style="info" %} Swap quote requests require a \`userPublicKey\` — a valid Solana wallet address. The server uses it to build transaction instructions scoped to that wallet. {% endhint %} The \[\`@titanexchange/sdk-ts\`\](/titan/developer-doc/resources/sdk.md) SDK supports Titan Direct (WebSocket) only. {% tabs %} {% tab title="Titan Direct" %} {% stepper %} {% step %} \*\*Install the SDK\*\* \`\`\`bash npm install @titanexchange/sdk-ts bs58 \`\`\` {% endstep %} {% step %} \*\*Set your credentials\*\* \`\`\`bash export TITAN\_ENDPOINT="wss://YOUR\_ENDPOINT/api/v1/ws" export TITAN\_API\_KEY="YOUR\_API\_TOKEN" \`\`\` {% endstep %} {% step %} \*\*Connect and get a quote\*\* \`\`\`typescript import { V1Client } from '@titanexchange/sdk-ts'; import bs58 from 'bs58'; const client = await V1Client.connect( \`${process.env.TITAN\_ENDPOINT}?auth=${process.env.TITAN\_API\_KEY}\` ); const { stream } = await client.newSwapQuoteStream({ swap: { inputMint: bs58.decode('So11111111111111111111111111111111111111112'), outputMint: bs58.decode('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'), amount: BigInt(1\_000\_000\_000), // 1 SOL in lamports slippageBps: 50, }, transaction: { userPublicKey: bs58.decode('YOUR\_WALLET\_PUBLIC\_KEY'), }, }); for await (const update of stream) { const quotes = update.quotes; if (!Object.keys(quotes).length) continue; for (const \[provider, route\] of Object.entries(quotes as Record)) { console.log(\`${provider}: ${route.outAmount} out\`); } break; // First update received — stop here } await client.close(); \`\`\` {% endstep %} {% endstepper %} {% endtab %} {% tab title="Titan Gateway" %} {% stepper %} {% step %} \*\*Install dependencies\*\* \`\`\`bash npm install @msgpack/msgpack \`\`\` {% endstep %} {% step %} \*\*Set your credentials\*\* \`\`\`bash export TITAN\_ENDPOINT="https://YOUR\_ENDPOINT" export TITAN\_API\_KEY="YOUR\_API\_TOKEN" \`\`\` {% endstep %} {% step %} \*\*Request a quote\*\* \`\`\`typescript import { decode } from '@msgpack/msgpack'; const params = new URLSearchParams({ inputMint: 'So11111111111111111111111111111111111111112', outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', amount: '1000000000', // 1 SOL in lamports userPublicKey: 'YOUR\_WALLET\_PUBLIC\_KEY', slippageBps: '50', }); const res = await fetch( \`${process.env.TITAN\_ENDPOINT}/api/v1/quote/swap?${params}\`, { headers: { 'Authorization': \`Bearer ${process.env.TITAN\_API\_KEY}\`, 'Accept': 'application/vnd.msgpack', }, } ); if (!res.ok) throw new Error(\`${res.status}: ${res.statusText}\`); const data = decode(new Uint8Array(await res.arrayBuffer())) as any; for (const \[provider, route\] of Object.entries(data.quotes as Record)) { console.log(\`${provider}: ${route.outAmount} out\`); } \`\`\` {% endstep %} {% endstepper %} {% endtab %} {% endtabs %} ## What a quote looks like The response contains a \`quotes\` map keyed by provider ID — \`"Titan"\`, \`"Metis"\`, \`"Okx"\`, etc. Each entry is a \`SwapRoute\` with everything you need to build and send a transaction: \* \*\*\`inAmount\`\*\* / \*\*\`outAmount\`\*\* — the input and output amounts for this route. \* \*\*\`slippageBps\`\*\* — the slippage tolerance applied to this quote. \* \*\*\`computeUnitsSafe\`\*\* — recommended compute budget that accounts for on-chain variance. \* \*\*\`instructions\`\*\* — the swap instructions to include in your transaction. \* \*\*\`addressLookupTables\`\*\* — ALT addresses needed to compile a V0 transaction. \* \*\*Quote expiry\*\* — if a route expires, \`expiresAtMs\` contains the expiry as a millisecond UNIX timestamp and \`expiresAfterSlot\` contains the last valid slot. Check these before executing if present. Not every provider appears in every response. Iterate with \`Object.entries(quotes)\` and pick the route with the best \`outAmount\`. ## Next steps \* \[Stream & Execute a Swap\](/titan/developer-doc/swap-api/guides/stream-and-execute.md) — full guide with transaction building, signing, and error handling \* \[Configure Routing\](/titan/developer-doc/swap-api/guides/configure-routing.md) — filter venues and providers, set account limits \* \[Authentication\](/titan/developer-doc/getting-started/authentication.md) — JWT claims reference --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides.md). # Guides \*\*Practical walkthroughs for the most common Swap API workflows.\*\* Each guide builds on the \[Quickstart\](/titan/developer-doc/swap-api/quickstart.md) and assumes you have a working connection. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/error-codes.md). # Error Codes Every error response has the same shape: \`\`\`json { "success": false, "error": { "code": "VALIDATION\_ERROR", "message": "Human-readable summary", "details": { "…": "optional, code-specific" } } } \`\`\` Always branch on \`error.code\`. The \`message\` is for humans and can change without notice. HTTP codes follow the usual split: \`2xx\` success, \`4xx\` client error, \`409\` race/state conflict, \`422\` idempotency mismatch, \`5xx\` server error. ## Auth & tenancy | HTTP | Code | Meaning | | ---- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------- | | 401 | \`UNAUTHORIZED\` | On a user-scoped route: \`X-Titan-User\` missing, or the supplied id was never onboarded — call \`POST /partner/onboard\` first. | | 401 | \`INVALID\_API\_KEY\` | Missing or unknown \`X-Titan-Key\`. | | 401 | \`KEY\_REVOKED\` | Your key was revoked. | | 401 | \`ENV\_MISMATCH\` | Key issued for a different environment. | | 403 | \`PRODUCT\_DISABLED\` | DCA product disabled on your key. | | 403 | \`PRODUCT\_EXPIRED\` | DCA grant expired. | | 403 | \`TENANT\_SUSPENDED\` | Tenant suspended (recoverable; contact Titan). | | 403 | \`TENANT\_DELETED\` | Tenant deleted. | | 403 | \`TENANT\_NOT\_PROVISIONED\` | Key valid, but no tenant row yet. | | 422 | \`IDEMPOTENCY\_KEY\_REUSED\` | Same \`X-Idempotency-Key\`, different body. | ## Onboarding (\`POST /partner/onboard\`) | HTTP | Code | Meaning | | --------- | --------------------------- | ------------------------------------------------------------------------------------------------- | | 400 | \`BAD\_REQUEST\` | Missing \`sub\` / \`userPubkey\` / \`siws.message\` / \`siws.signature\`, or invalid JSON. | | 400 | \`SIWS\_INVALID\` | Signature, canonical-form, freshness, or ownership check failed. | | 400 | \`PARTNER\_NOT\_CONFIGURED\` | Tenant isn't enabled for partner onboarding. | | 409 | \`USER\_PUBKEY\_CONFLICT\` | \`sub\` and the attested wallet resolve to two different Titan identities. | | 409 | \`WALLET\_NEEDS\_USER\_CONSENT\` | Wallet exists with no DCA setup and Titan can't attach one server-side (rare). | | 409 | \`ONBOARDING\_INCOMPLETE\` | A user-scoped call hit a not-fully-provisioned manager. Re-call onboard (idempotent), then retry. | | 500 / 502 | \`PROVISIONING\_FAILED\` | Provisioning error (\`502\` upstream, \`500\` unexpected). Safe to retry. | ## Orders & transactions | HTTP | Code | Where / when | | ---- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 400 | \`VALIDATION\_ERROR\` | Most invalid bodies / query params. \`details\` usually has a per-field breakdown. | | 400 | \`MISSING\_PARAMS\` | \`POST /orders/confirm\` — required body fields absent. | | 400 | \`INVALID\_REQUEST\` | \`POST /dca/{id}/modify/confirm\` — body shape invalid. | | 400 | \`INVALID\_ORDER\_TYPE\` | \`orderType\` not \`dca\`. | | 400 | \`INVALID\_MINTS\` | Same input/output mint. | | 400 | \`MIN\_NOTIONAL\_NOT\_MET\` | Per-cycle value below your tenant's minimum ($10 USD default). \`details\`: \`mint\`, \`amount\`, \`usdCents\`, \`requiredCents\`. | | 400 | \`INVALID\_TRANSACTION\` | Confirm-step tx failed structural checks. | | 400 | \`INVALID\_STATUS\` | Pending order already consumed (e.g. previously confirmed). | | 400 | \`EXPIRED\` | Pending order's 5-minute window elapsed (\`/orders/confirm\`). | | 400 | \`TRANSACTION\_EXPIRED\` | The unsigned transaction's 5-minute window elapsed. Request a new intent. | | 400 | \`TRANSACTION\_TAMPERED\` | Submitted signed tx doesn't match the unsigned one Titan returned. | | 400 | \`INVALID\_STATE\` | Atomic transition race, or unsupported order state. | | 400 | \`ORDER\_NOT\_FAILED\` | \`POST /orders/{id}/retry\` on a non-\`failed\` order. \`details.status\` echoes current status. | | 400 | \`INSUFFICIENT\_FUNDS\` | Wallet now lacks input mint on retry. \`details\`: \`mint\`, \`required\`, \`currentBalance\`, \`gap\`. | | 400 | \`NOT\_CANCELLABLE\` | \`POST /orders/{id}/cancel\` on an order that can't be cancelled in its state. | | 400 | \`MODIFICATION\_ERROR\` | Modify rejected by a server-side guard, or \`cyclesCompleted\` mismatch. Re-fetch, re-run modify-intent. | | 409 | \`USER\_PUBKEY\_CONFLICT\` | \`POST /orders/intent\` with \`onboardIfNeeded: true\` — the wallet already belongs to a Titan account (yours, the Titan app's, or another partner's). Fall back to the two-step SIWS flow via \`POST /partner/onboard\`. | | 403 | \`FORBIDDEN\` | Pending order on \`/orders/confirm\` belongs to a different user/tenant. | | 404 | \`NOT\_FOUND\` | Resource doesn't exist or isn't yours / this user's. | | 409 | \`EXECUTION\_IN\_FLIGHT\` | A swap attempt is still reconciling. Self-resolving — retry shortly. | | 409 | \`LOCK\_FAILED\` | Concurrent modification on the same order at \`PATCH /dca/{id}\`. | | 409 | \`LOCK\_EXPIRED\` | The 30-second modify lock elapsed before \`/modify/confirm\`. Re-run \`PATCH /dca/{id}\`. | ## Withdrawals | HTTP | Code | Where / when | | ---- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | 400 | \`INSUFFICIENT\_AVAILABLE\` | Wallet-level: entire balance locked or wallet empty. | | 400 | \`FUNDS\_LOCKED\` | Wallet-level: \`requested > available\`. \`details\` includes the lock breakdown. | | 400 | \`INSUFFICIENT\_BALANCE\` | Wallet-level: \`requested > chainBalance\`. | | 400 | \`NOTHING\_TO\_WITHDRAW\` | Order-level: no order-owned funds remain. | | 400 | \`ALREADY\_WITHDRAWN\` | Order-level: a prior withdrawal already completed (including a failed order's auto-return). | | 409 | \`WITHDRAWAL\_IN\_PROGRESS\` / \`WITHDRAWAL\_IN\_FLIGHT\` | Finish or abandon the pending withdrawal first. | | 500 | \`WITHDRAWAL\_BUILD\_FAILED\` | Cancel succeeded but the withdrawal tx failed to build. The order is \`cancelled\`; call \`POST /orders/{id}/withdraw\` to retry the withdrawal leg. | ## Server & infrastructure | HTTP | Code | Meaning | | ---- | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | 500 | \`RPC\_ERROR\` | Couldn't reach Solana (e.g. on \`GET /me/balance\`). | | 500 | \`SUBMIT\_FAILED\` / \`CONFIRMATION\_FAILED\` / \`ORDER\_FINALIZE\_FAILED\` | Failure during submit / on-chain confirm / order finalization. Safe to retry — use idempotency on submit-style calls. | | 500 | \`TX\_FAILED\` | Submitted tx didn't confirm on Solana. | | 500 | \`INTERNAL\_ERROR\` | Unexpected server error. | | 503 | \`RPC\_UNAVAILABLE\` | RPC temporarily unavailable. Retry shortly. | | 503 | \`PRICE\_ORACLE\_UNAVAILABLE\` | Couldn't price a non-stable input mint to enforce the per-cycle minimum. Retry shortly. | ## Handling strategy Treat \`4xx\` codes as actionable by your integration — fix the request, re-onboard, or surface a message to the user. Treat \`409\` as a transient race: back off a few seconds and retry. Treat \`5xx\` and \`503\` as retryable server-side issues, and attach an \`X-Idempotency-Key\` to any submit-style POST you retry so a success that you didn't see the response for isn't re-applied. See \[Limits & Idempotency\](/titan/developer-doc/dca-partner-api/reference/limits.md#idempotency). ## Related pages \* \[Authentication\](/titan/developer-doc/dca-partner-api/reference/authentication.md) — the auth/tenancy codes in context \* \[Create & Manage Orders\](/titan/developer-doc/dca-partner-api/guides/orders.md) — per-endpoint error handling for the order flows \* \[Limits & Idempotency\](/titan/developer-doc/dca-partner-api/reference/limits.md) — retry windows and the idempotency contract --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/stream-and-execute.md). # Stream & Execute a Swap This guide covers \*\*Titan Direct\*\* — the WebSocket path. For a single-request flow using Titan Gateway, see the \[Quickstart\](/titan/developer-doc/swap-api/quickstart.md). It walks through the complete lifecycle — connecting over WebSocket, streaming live quotes, picking the best one, building and signing a transaction, then shutting down cleanly. This guide uses raw WebSocket and MessagePack directly. If you prefer a higher-level interface, see the \[\`@titanexchange/sdk-ts\`\](/titan/developer-doc/resources/sdk.md) SDK. {% hint style="info" %} You need an API token and endpoint URL before starting. See \[Get API Access\](/titan/developer-doc/getting-started/api-access.md) if you don't have one yet. {% endhint %} ## Prerequisites \`\`\`bash npm install ws @msgpack/msgpack bs58 @solana/web3.js http-encoding \`\`\` Set your environment variables: \`\`\`bash export TITAN\_ENDPOINT="wss://YOUR\_ENDPOINT/api/v1/ws" export TITAN\_API\_KEY="YOUR\_API\_TOKEN" export SOLANA\_RPC\_URL="https://YOUR\_RPC\_ENDPOINT" \`\`\` \*\*\* {% stepper %} {% step %} \*\*Connect and negotiate the protocol\*\* Titan Direct uses \[MessagePack\](/titan/developer-doc/swap-api/reference/wire-protocol.md) over WebSocket. List your supported compression schemes in the \[\`Sec-WebSocket-Protocol\`\](/titan/developer-doc/swap-api/reference/direct/connection.md) header — the server selects the best match and confirms it on open. \`\`\`typescript import WebSocket from 'ws'; import { Encoder, Decoder } from '@msgpack/msgpack'; import bs58 from 'bs58'; import { zstdCompress, zstdDecompress, brotliCompress, brotliDecompress, gzipCompress, gzipDecompress, } from 'http-encoding'; // useBigInt64 ensures amounts encode as int64, not float64 const encoder = new Encoder({ useBigInt64: true }); const decoder = new Decoder({ useBigInt64: true }); // Build the WebSocket URL with auth token as query param const url = \`${process.env.TITAN\_ENDPOINT}?auth=${process.env.TITAN\_API\_KEY}\`; // List supported protocols in preference order — server picks the best match // zstd gives the best compression ratio, fallback to brotli, gzip, or none const ws = new WebSocket(url, \[\ 'v1.api.titan.ag+zstd', // preferred — best ratio + speed\ 'v1.api.titan.ag+brotli', // fallback\ 'v1.api.titan.ag+gzip', // fallback\ 'v1.api.titan.ag', // no compression\ \]); // Compress/decompress default to identity (no-op) — overwritten on open let compress: (data: Uint8Array) => Promise | Uint8Array = (d) => d; let decompress: (data: Uint8Array) => Promise | Uint8Array = (d) => d; let requestId = 0; ws.on('open', () => { // The server confirms which protocol it selected via ws.protocol const proto = ws.protocol; console.log('Connected — protocol:', proto); // Match the negotiated protocol to the correct codec if (proto.endsWith('+zstd')) { compress = zstdCompress; decompress = zstdDecompress; } else if (proto.endsWith('+brotli')) { compress = brotliCompress; decompress = brotliDecompress; } else if (proto.endsWith('+gzip')) { compress = gzipCompress; decompress = gzipDecompress; } // If none matched, no compression — identity functions stay in place }); // Encode a request as MessagePack, compress, and send async function sendRequest(data: Record): Promise { const id = requestId++; const encoded = encoder.encode({ id, data }); ws.send(await compress(encoded)); return id; } // Decompress an incoming binary frame and decode from MessagePack async function decodeMessage(raw: Buffer): Promise { const data = await decompress(raw); return decoder.decode(data); } ws.on('error', (err) => { console.error('WebSocket error:', err.message); }); \`\`\` {% endstep %} {% step %} \*\*Call GetInfo\*\* Send \[\`GetInfo\`\](/titan/developer-doc/swap-api/reference/direct/get-info.md) right after connecting to confirm the connection and read the server's current defaults — update interval, slippage bounds, and stream limits. \`\`\`typescript ws.on('open', () => { sendRequest({ GetInfo: {} }); }); ws.on('message', async (raw: Buffer) => { const msg = await decodeMessage(raw); if ('Response' in msg && 'GetInfo' in msg.Response.data) { const info = msg.Response.data.GetInfo; console.log('Protocol version:', info.protocolVersion); console.log('Default update interval:', info.settings.quoteUpdate.intervalMs.default, 'ms'); } }); \`\`\` {% endstep %} {% step %} \*\*Open a quote stream\*\* Send \[\`NewSwapQuoteStream\`\](/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream.md) to start receiving live quotes. The \`swap\` object defines what to quote, \`transaction\` provides the wallet context needed to build executable instructions. \`\`\`typescript const SOL = bs58.decode('So11111111111111111111111111111111111111112'); const USDC = bs58.decode('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); const userPublicKey = bs58.decode('YOUR\_WALLET\_PUBLIC\_KEY'); sendRequest({ NewSwapQuoteStream: { swap: { inputMint: SOL, outputMint: USDC, amount: 1\_000\_000\_000n, // 1 SOL in lamports slippageBps: 50, }, transaction: { userPublicKey, }, }, }); \`\`\` The server responds with a \`stream.id\` — save it to stop the stream later. {% hint style="warning" %} Use \`BigInt\` for \`amount\`. Numbers above 2^32 encode as float64 in MessagePack, which the server rejects. {% endhint %} {% endstep %} {% step %} \*\*Read quotes and pick the best\*\* The server pushes \[\`StreamData\`\](/titan/developer-doc/swap-api/reference/types.md) messages at the negotiated interval. Each update includes \`metadata.ExpectedWinner\` — \*\*Titan's recommendation for the best slippage-adjusted route.\*\* \`\`\`typescript let streamId: number | undefined; ws.on('message', async (raw: Buffer) => { const msg = await decodeMessage(raw); if ('Response' in msg && 'NewSwapQuoteStream' in msg.Response.data) { streamId = msg.Response.stream.id; console.log(\`Stream ${streamId} open\`); return; } if ('StreamData' in msg) { const swapQuotes = msg.StreamData.payload.SwapQuotes; // Use metadata.ExpectedWinner for the best slippage-adjusted route const winner = swapQuotes.metadata?.ExpectedWinner; const bestRoute = winner && swapQuotes.quotes\[winner\]; if (bestRoute?.instructions?.length) { console.log(\`Best: ${winner} — ${bestRoute.outAmount} out\`); } } }); \`\`\` {% endstep %} {% step %} \*\*Build, sign, and send\*\* Each quote returns \`instructions\` and \`addressLookupTables\` as part of the \[\`SwapRoute\`\](/titan/developer-doc/swap-api/reference/types.md) type. Fetch the ALT accounts, compile a V0 message, sign, and send. Use \`computeUnitsSafe\` for your compute budget — it accounts for on-chain variance and gives your transaction room to land without failing on a slightly heavier slot. \`\`\`typescript import { Connection, VersionedTransaction, TransactionMessage, AddressLookupTableAccount, TransactionInstruction, PublicKey, } from '@solana/web3.js'; const connection = new Connection(process.env.SOLANA\_RPC\_URL!); function toSolanaInstruction(ix: any): TransactionInstruction { return new TransactionInstruction({ programId: new PublicKey(ix.p), keys: ix.a.map((acc: any) => ({ pubkey: new PublicKey(acc.p), isSigner: acc.s, isWritable: acc.w, })), data: Buffer.from(ix.d), }); } async function executeQuote(route: any): Promise { const altAccounts: AddressLookupTableAccount\[\] = \[\]; for (const altPubkey of (route.addressLookupTables ?? \[\])) { const { value } = await connection.getAddressLookupTable(new PublicKey(altPubkey)); if (value) altAccounts.push(value); } const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash(); const message = new TransactionMessage({ payerKey: new PublicKey(userPublicKey), recentBlockhash: blockhash, instructions: route.instructions.map(toSolanaInstruction), }).compileToV0Message(altAccounts); const tx = new VersionedTransaction(message); // Sign the transaction — use your wallet adapter or Keypair // Client-side: const signed = await signTransaction(tx); // Server-side: tx.sign(\[keypair\]); try { const sig = await connection.sendRawTransaction(tx.serialize()); await connection.confirmTransaction({ signature: sig, blockhash, lastValidBlockHeight }); console.log('Confirmed:', sig); if (streamId !== undefined) { sendRequest({ StopStream: { id: streamId } }); } } catch (err: any) { console.error('Send failed:', err.message); } } \`\`\` {% hint style="warning" %} If a route expires, \`expiresAtMs\` contains the expiry as a millisecond UNIX timestamp and \`expiresAfterSlot\` contains the last slot at which the route is valid. If present, check these before executing — a route may no longer be valid by the time your transaction lands on-chain. {% endhint %} {% endstep %} {% step %} \*\*Stop the stream\*\* Once you've executed, send \[\`StopStream\`\](/titan/developer-doc/swap-api/reference/direct/stop-stream.md) to free up the connection for other streams. \`\`\`typescript sendRequest({ StopStream: { id: streamId! } }); \`\`\` {% endstep %} {% step %} \*\*Handle StreamEnd and errors\*\* \`\`\`typescript ws.on('message', async (raw: Buffer) => { const msg = await decodeMessage(raw); if ('Error' in msg) { console.error(\`Request ${msg.Error.requestId} failed \[${msg.Error.code}\]: ${msg.Error.message}\`); } if ('StreamEnd' in msg) { const { id, errorCode, errorMessage } = msg.StreamEnd; if (errorCode) { console.error(\`Stream ${id} error ${errorCode}: ${errorMessage}\`); } else { console.log(\`Stream ${id} closed\`); } ws.close(); } }); \`\`\` For reconnect patterns see \[Error Handling & Reconnect\](/titan/developer-doc/swap-api/guides/error-handling.md). {% endstep %} {% endstepper %} \*\*\* ## Key details \* \*\*\`quotes\` shape\*\* — \`Record\` keyed by provider ID, not an array. \* \*\*Transaction building\*\* — Build a V0 transaction from \`instructions\` + \`addressLookupTables\`. \* \*\*Compute budget\*\* — Use \`computeUnitsSafe\` — accounts for on-chain variance. \* \*\*Quote expiry\*\* — If a route expires, \`expiresAtMs\` and \`expiresAfterSlot\` will be set. Check these before executing. \*\*\* ## Related pages \* \[Configure Routing\](/titan/developer-doc/swap-api/guides/configure-routing.md) — filter venues and providers \* \[Error Handling & Reconnect\](/titan/developer-doc/swap-api/guides/error-handling.md) — error codes and reconnect patterns \* \[Wire Protocol\](/titan/developer-doc/swap-api/reference/wire-protocol.md) — MessagePack encoding and compression \* \[Connection & Negotiation\](/titan/developer-doc/swap-api/reference/direct/connection.md) — protocol negotiation details \* \[Types Reference\](/titan/developer-doc/swap-api/reference/types.md) — \`SwapRoute\`, \`StreamData\`, and all type definitions \* \[NewSwapQuoteStream Reference\](/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream.md) \* \[CPI Example (Anchor)\](https://github.com/Titan-Pathfinder/titan-v2-cpi-example) — Example Anchor program demonstrating cross-program invocation into Titan's \`swap\_route\_v2\` instruction --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol.md). # Wire Protocol This page covers how data is encoded on the wire — the serialization format, encoding conventions, and shared types used across all Titan API messages. \*\*Both Titan Direct and Titan Gateway use MessagePack binary encoding. JSON is not supported.\*\* ## Data format The basic data format for serialization of all messages is \*\*MessagePack\*\*. \* \*\*Objects/structs are encoded as maps\*\* — this allows additional fields to be added without breaking compatibility with previous versions. \* \*\*Field names are \`camelCase\`\*\* unless otherwise specified. \* \*\*Integers are encoded using the smallest MessagePack int type\*\* that fits the value. \* \*\*Use \`BigInt\` for \`u64\` values\*\* (amounts, timestamps) — values above 2^53 lose precision as float64, which the server rejects. ## Optional data If a value is optional, its type is \`Option\` in Rust and \`T?\` or \`T | null\` in TypeScript. \*\*Optional fields in objects may be omitted entirely from the serialized map.\*\* Otherwise, a missing optional value should be encoded as \`nil\` (\`0xc0\`) — decoded as \`None\` in Rust and \`null\` in TypeScript. ## Simple enumerations Simple enumerations (those without associated data) are \*\*encoded as strings matching the variant name exactly\*\*: {% tabs %} {% tab title="Rust" %} \`\`\`rust enum SwapMode { ExactIn, ExactOut, } // Encoded as: "ExactIn" or "ExactOut" \`\`\` {% endtab %} {% tab title="TypeScript" %} \`\`\`typescript enum SwapMode { ExactIn = "ExactIn", ExactOut = "ExactOut", } \`\`\` {% endtab %} {% endtabs %} ## Complex enumerations Complex enumerations (those with associated data) are \*\*encoded as single-value maps\*\*, mapping the variant name to the associated data. \* Single associated item → the value is that data. \* Multiple associated items → the value is an array. {% tabs %} {% tab title="Rust" %} \`\`\`rust struct Request2Data { id: u32, amount: u64, } enum Complex { Request1(String), Request2(Request2Data), Request3(u32, u32), } // Valid encodings (shown as JSON for readability): // { "Request1": "hello" } // { "Request2": {"id": 1, "amount": 34} } // { "Request3": \[3, 4\] } \`\`\` {% endtab %} {% tab title="TypeScript" %} \`\`\`typescript interface Request2Data { id: number; amount: number; } type Complex = | { Request1: string } | { Request2: Request2Data } | { Request3: \[number, number\] }; \`\`\` {% endtab %} {% endtabs %} This pattern applies to both client requests (\`RequestData\`) and server messages (\`ServerMessage\`). \*\*To determine the message type, check which key is present in the top-level map.\*\* ## Binary data Binary data is encoded using MessagePack \`bin\` formats. {% tabs %} {% tab title="Rust" %} \`\`\`rust // Variable-sized byte array Vec // Fixed-size byte array (N bytes) \[u8; N\] \`\`\` {% endtab %} {% tab title="TypeScript" %} \`\`\`typescript // Binary data in TypeScript Uint8Array // or ArrayBuffer \`\`\` {% endtab %} {% endtabs %} TypeScript has no way to specify byte array size — refer to the Rust types for size constraints. \*\*\* ## Common types ### Pubkey \*\*Solana public keys are 32-byte binary data.\*\* Encoded using MessagePack \`bin 8\` format — all pubkeys start with \`c4 20\` followed by 32 bytes of key data. Example — the WSOL public key \`So11111111111111111111111111111111111111112\`: \`\`\` c4 20 069b8857feab8184fb687f634618c035dac439dc1aeb3b5598a0f00000000001 \`\`\` {% tabs %} {% tab title="Rust" %} \`\`\`rust // Type alias for public keys type Pubkey = \[u8; 32\]; \`\`\` {% endtab %} {% tab title="TypeScript" %} \`\`\`typescript // Type alias for public keys to differentiate from other binary data type Pubkey = Uint8Array; // 32 bytes \`\`\` {% endtab %} {% endtabs %} ### AccountMeta Compact account descriptor used in instructions. \*\*Uses single-letter field names to minimize message size.\*\* {% tabs %} {% tab title="Rust" %} \`\`\`rust struct AccountMeta { p: Pubkey, // public key s: bool, // is\_signer w: bool, // is\_writable } \`\`\` {% endtab %} {% tab title="TypeScript" %} \`\`\`typescript interface AccountMeta { p: Pubkey; // public key s: boolean; // is\_signer w: boolean; // is\_writable } \`\`\` {% endtab %} {% endtabs %} ### Instruction A single on-chain instruction. \*\*Also uses single-letter field names for compactness.\*\* {% tabs %} {% tab title="Rust" %} \`\`\`rust struct Instruction { p: Pubkey, // program\_id a: Vec, // accounts d: Vec, // data } \`\`\` {% endtab %} {% tab title="TypeScript" %} \`\`\`typescript interface Instruction { p: Pubkey; // program\_id a: AccountMeta\[\]; // accounts d: Uint8Array; // data } \`\`\` {% endtab %} {% endtabs %} {% hint style="info" %} \*\*\`AccountMeta\` and \`Instruction\` use single-letter field names (\`p\`, \`s\`, \`w\`, \`a\`, \`d\`) to reduce payload size.\*\* These are Titan's wire format, not abbreviations of the standard Solana SDK types. {% endhint %} \*\*\* ## Message envelope types ### ClientRequest Every client request wraps an RPC method call with a monotonically increasing \`id\`. {% tabs %} {% tab title="Rust" %} \`\`\`rust struct ClientRequest { /// Request ID, echoed in the server's response. id: u32, /// One of the RPC method variants. data: RequestData, } enum RequestData { GetInfo(GetInfoRequest), NewSwapQuoteStream(SwapQuoteRequest), StopStream(StopStreamRequest), GetVenues(GetVenuesRequest), ListProviders(ListProvidersRequest), GetSwapPrice(SwapPriceRequest), } \`\`\` {% endtab %} {% tab title="TypeScript" %} \`\`\`typescript interface ClientRequest { // Request ID, echoed in the server's response. id: number; // One of the RPC method variants. data: RequestData; } type RequestData = | { GetInfo: GetInfoRequest } | { NewSwapQuoteStream: SwapQuoteRequest } | { StopStream: StopStreamRequest } | { GetVenues: GetVenuesRequest } | { ListProviders: ListProvidersRequest } | { GetSwapPrice: SwapPriceRequest }; \`\`\` {% endtab %} {% endtabs %} ### ServerMessage \*\*The server sends one of four message types:\*\* {% tabs %} {% tab title="Rust" %} \`\`\`rust /// A message sent by the server to the client. enum ServerMessage { /// Successful response to a request, may optionally start a stream. Response(ResponseSuccess), /// An error response to a request. Error(ResponseError), /// Data for a stream. StreamData(StreamData), /// Notification that a stream has ended. StreamEnd(StreamEnd), } /// A successful response. struct ResponseSuccess { /// Identifier of the request that triggered this response. requestId: u32, /// The response data. data: ResponseData, /// If this request starts a new stream, contains stream info. stream: Option, } /// An error response. struct ResponseError { /// Identifier of the request that triggered this response. requestId: u32, /// A numeric error code. code: u32, /// A message describing the error. message: String, } /// Data packet for a stream. struct StreamData { /// ID of the stream. id: u32, /// Sequence number of this data packet. seq: u32, /// Data payload. payload: StreamDataPayload, } /// Notification that a stream has closed. struct StreamEnd { /// ID of the stream that has ended. id: u32, /// Error code, if the stream ended abnormally. errorCode: Option, /// Error message, if the stream ended abnormally. errorMessage: Option, } /// Notification that a new stream has been started. struct StreamStart { /// Stream ID — present in all StreamData and StreamEnd for this stream. id: u32, /// Type of data that will be sent in this stream. dataType: StreamDataType, } enum StreamDataType { SwapQuotes, // May be expanded in the future. } enum StreamDataPayload { SwapQuotes(SwapQuotes), // May be expanded in the future. } enum ResponseData { GetInfo(ServerInfo), NewSwapQuoteStream(QuoteSwapStreamResponse), StreamStopped(StopStreamResponse), GetVenues(VenueInfo), ListProviders(Vec), GetSwapPrice(SwapPrice), } \`\`\` {% endtab %} {% tab title="TypeScript" %} \`\`\`typescript type ServerMessage = | { Response: ResponseSuccess } | { Error: ResponseError } | { StreamData: StreamData } | { StreamEnd: StreamEnd }; interface ResponseSuccess { // Identifier of the request that triggered this response. requestId: number; // The response data. data: ResponseData; // If the request started a new stream, contains stream info. stream?: StreamStart; } interface ResponseError { // Identifier of the request that triggered this response. requestId: number; // A numeric error code. code: number; // A message describing the error. message: string; } interface StreamData { // ID of the stream. id: number; // Sequence number of this data packet. seq: number; // Data payload. payload: StreamDataPayload; } interface StreamEnd { // ID of the stream that has ended. id: number; // Error code, if the stream ended abnormally. errorCode?: number; // Error message, if the stream ended abnormally. errorMessage?: string; } interface StreamStart { // Stream ID. id: number; // Type of data that will be sent in this stream. dataType: StreamDataType; } enum StreamDataType { SwapQuotes = "SwapQuotes", } type StreamDataPayload = { SwapQuotes: SwapQuotes }; type ResponseData = | { GetInfo: ServerInfo } | { NewSwapQuoteStream: QuoteSwapStreamResponse } | { StreamStopped: StopStreamResponse } | { GetVenues: VenueInfo } | { ListProviders: ProviderInfo\[\] } | { GetSwapPrice: SwapPrice }; \`\`\` {% endtab %} {% endtabs %} \*\*\* ## Compression Compression wraps the MessagePack payload. The order of operations: \*\*Sending:\*\* serialize to MessagePack → compress → send as binary WebSocket frame \*\*Receiving:\*\* receive binary frame → decompress → deserialize from MessagePack The compression scheme is negotiated once at connection time. See \[Connection & Negotiation\](/titan/developer-doc/swap-api/reference/direct/connection.md) for protocol strings and setup. ## Gateway differences Titan Gateway uses the same MessagePack encoding but over HTTP REST: \* \*\*Requests\*\* — query parameters (pubkeys as Base58 strings, not binary) \* \*\*Responses\*\* — MessagePack body with \`Content-Type: application/vnd.msgpack\` \* \*\*Pubkeys in responses\*\* are still binary \`Uint8Array\` in the MessagePack body \*\*\* ## Related pages \* \[Connection & Negotiation\](/titan/developer-doc/swap-api/reference/direct/connection.md) — WebSocket setup, authentication, and compression negotiation \* \[GetInfo\](/titan/developer-doc/swap-api/reference/direct/get-info.md) — server settings and protocol version \* \[NewSwapQuoteStream\](/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream.md) — streaming swap quotes --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/get-api-access.md). # Get API Access ## Free public endpoint A public DART endpoint is available for testing and low-volume use. No API key required. \*\*Base URL:\*\* \`https://api.titan.exchange/dart\` \* \*\*1 request per second\*\* per IP address \* REST only, JSON responses \* DART provider only \*\*\* ## Partner access For higher rate limits, pass your API key via header: \`\`\`bash curl -X POST https://api.titan.exchange/dart/swap \\ -H "Authorization: Bearer YOUR\_API\_KEY" \\ -H "Content-Type: application/json" \\ -d '{ ... }' \`\`\` Or using \`X-API-Key\`: \`\`\`bash curl -X POST https://api.titan.exchange/dart/swap \\ -H "X-API-Key: YOUR\_API\_KEY" \\ -H "Content-Type: application/json" \\ -d '{ ... }' \`\`\` To request a partner API key, fill out the application form: \[\*\*Apply for DART API Access\*\*\](https://tally.so/r/1AvYeL) \*\*\* ## Related pages \* \[Overview\](/titan/developer-doc/dart-swap-api/overview.md) — What DART is, supported pairs, and fees \* \[How to Use\](/titan/developer-doc/dart-swap-api/how-to-use.md) — Endpoints, request/response format, and transaction building --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/overview.md). # Overview > \*\*Beta\*\* — The DART API is currently in beta. The DART API provides swap quotes for selected token pairs on Solana via Titan's \*\*DART (Dynamically Allocated Real Time)\*\* engine. This is a dedicated DART-only endpoint — it exclusively uses the Titan DART provider for routing. DART dynamically re-optimizes trades at the exact moment of execution, not just at quote time — \*\*guaranteeing best execution when it matters most.\*\* \*\*Base URL:\*\* \`https://api.titan.exchange/dart\` \*\*Free to use\*\* · No API key required · JSON responses · Up to 1 bps fee · 1 req/sec rate limit \*\*\* ## Supported pairs Use \`GET /markets\` to discover pairs programmatically, or reference the list below. \* SOL/USDC \* SOL/USDT \* USDT/USDC \* cbBTC/USDC \* wETH/USDC \* TRUMP/USDC \* ZEC/USDC \* USD1/USDC \* HYPE/USDC \* PUMP/USDC \* PENGU/USDC \* FARTCOIN/USDC \* syrupUSD/USDC \* PYUSD/USDC \* USDG/USDC \* CASH/USDC \* AAVE/USDC \* MEGA/USDC \* SPCX/USDC \* MU/USDC The \`/swap\` endpoint works on supported token pairs listed above. \*\*\* ## Rate limits & fees \* \*\*1 request per second\*\* per IP address. HTTP 429 if exceeded. \* \*\*Up to 1 bps fee\*\* per swap, handled by the on-chain program. \*\*\* ## Related pages \* \[Get API Access\](/titan/developer-doc/dart-swap-api/get-api-access.md) — Free vs partner access, API keys \* \[How to Use\](/titan/developer-doc/dart-swap-api/how-to-use.md) — Endpoints, request/response format, and transaction building --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/endpoints.md). # Endpoints The full route contract. Paths are relative to your environment base URL. The auth tier per route is in \[Authentication\](/titan/developer-doc/dca-partner-api/reference/authentication.md); the complete error catalog is in \[Error Codes\](/titan/developer-doc/dca-partner-api/reference/error-codes.md). Every response uses the envelope \`{ "success": true, "data": … }\` on success and \`{ "success": false, "error": { "code", "message", "details" } }\` on failure. The one exception is \`GET /health\`. ## Public ### \`GET /health\` Liveness probe. No auth. Returns \`200\` with \`status: "ok"\` or \`status: "degraded"\`, or \`503\` with a flat \`error\` string if the check fails. This is the only endpoint that doesn't use the standard envelope. ## Onboarding ### \`POST /partner/onboard\` \`X-Titan-Key\` only. Provisions a user's manager from a one-time SIWS signature. Idempotent. Full walkthrough in \[Onboarding\](/titan/developer-doc/dca-partner-api/guides/onboarding.md). \`\`\`json // Request { "sub": "", "userPubkey": "", "siws": { "message": "", "signature": "" } } // Response { "success": true, "data": { "userId": "", "walletAddress": "" } } \`\`\` ## User session ### \`GET /me\` \`X-Titan-Key\` + \`X-Titan-User\`. Resolves the current user. Returns \`{ userId, walletAddress, sessionId }\` (\`sessionId\` is always empty for partners). ## Balances ### \`GET /me/balance?hideZero=false\` Per-mint view of the manager, split into total / locked / available. \`hideZero=true\` drops rows where all buckets are zero. \`\`\`json { "success": true, "data": { "walletAddress": "GZk2v…", "balances": \[\ {\ "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",\ "symbol": "USDC", "decimals": 6, "programId": "token",\ "totalBalance": "1000000", "lockedForFutureTxns": "600000",\ "withdrawalPending": "0", "availableToWithdraw": "400000",\ "lockedBreakdown": \[\ { "orderId": "…", "orderType": "dca", "status": "active", "kind": "locked", "amount": "600000" }\ \]\ }\ \] } } \`\`\` Field semantics are in \[Order & Execution Schema\](/titan/developer-doc/dca-partner-api/reference/schema.md#balance-row). Errors: \`401 UNAUTHORIZED\`, \`500 RPC\_ERROR\`. ## DCA orders — create Always two steps. \`intent\` returns an unsigned deposit tx; \`confirm\` submits the signed tx and activates the order. ### \`POST /orders/intent\` User-scoped. Optional \`X-Idempotency-Key\`. The full request body and field table are in \[Create & Manage Orders\](/titan/developer-doc/dca-partner-api/guides/orders.md#create-intent-then-confirm). Pass \`onboardIfNeeded: true\` to provision a brand-new user inline and skip the separate SIWS step — see \[Single-transaction onboarding\](/titan/developer-doc/dca-partner-api/guides/onboarding.md#single-transaction-onboarding). \`\`\`json // Response { "success": true, "data": { "pendingOrderId": "9b3f1ad0-…", "memoId": "9b3f1ad0-…", "transaction": "", "encoding": "base64", "expiresAt": "2025-01-01T00:05:00.000Z", "orderType": "dca", "outputRecipientAddress": "", "inputMint": "So111…", "inputAmount": "1000000000", "feeLamports": "0" } } \`\`\` \`feeLamports\` is \`"0"\` when the deployment sponsors execution fees (the default for partner deployments); \`"5000000"\` (0.005 SOL) on non-sponsored deployments, included in the deposit tx. ### \`POST /orders/confirm\` \`\`\`json // Request { "pendingOrderId": "9b3f1ad0-…", "signedTransaction": "" } // Response { "success": true, "data": { "order": { "…": "…" }, "txSignature": "", "pendingOrderId": "9b3f1ad0-…" } } \`\`\` After \`200\`, the order is \`active\` and Titan runs each cycle automatically. ## DCA orders — read | Endpoint | Returns | | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \`GET /me/orders?status={status}&type={type}\` | All of the user's orders. \`status\`: \`pending\` \\| \`active\` \\| \`executing\` \\| \`pending\_modification\` \\| \`paused\` \\| \`completed\` \\| \`cancelled\` \\| \`failed\` \\| \`expired\`. \`type\`: \`dca\`. | | \`GET /me/orders/active\` | Currently running orders. | | \`GET /me/orders/history\` | Terminal orders (\`completed\` / \`cancelled\` / \`failed\` / \`expired\`). | | \`GET /orders/pending\` | Orders awaiting confirmation (signed tx not yet submitted, or in flight). | | \`GET /orders/pending/failed\` | Pending orders that timed out or failed before activating. | | \`GET /orders/pending/history\` | All pending-order rows, any status, most recent first. \*\*Capped at 50 rows.\*\* | | \`GET /dca/{orderId}\` | One DCA order with full progress. Full schema in \[Order & Execution Schema\](/titan/developer-doc/dca-partner-api/reference/schema.md#order). | | \`GET /orders/{orderId}/executions\` | Per-cycle execution history, most recent first. Capped at 500 rows. | \`GET /dca/{orderId}\` returns \`404 NOT\_FOUND\` if the order doesn't exist, isn't a DCA order, or isn't this user's. ## DCA orders — modify, pause, resume, retry, cancel | Endpoint | What it does | | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | \`PATCH /dca/{orderId}\` | Modify an \`active\`/\`paused\` order. Returns \`requiresTransaction: false\` for in-place edits, or \`true\` with an unsigned tx when \`totalAmount\` changes. | | \`POST /dca/{orderId}/modify/confirm\` | Submit the signed modify tx with the matching \`cyclesCompleted\` + \`config\`. | | \`POST /orders/{orderId}/pause\` | Pause an \`active\` order. | | \`POST /orders/{orderId}/resume\` | Resume a \`paused\` order. | | \`POST /orders/{orderId}/retry\` | Flip a \`failed\` order back to \`active\` (only before auto-return runs). | | \`POST /orders/{orderId}/cancel\` | Cancel; discriminated on \`withdraw\` — \`{ withdraw: false }\` or \`{ withdraw: true, userPubkey }\`. | The flows, gotchas, and per-endpoint error codes are in \[Create & Manage Orders\](/titan/developer-doc/dca-partner-api/guides/orders.md). ## Wallet-level withdrawals | Endpoint | What it does | | ---------------------------- | ----------------------------------------------------------------------------------------------------------- | | \`POST /withdraw/transaction\` | Build an unsigned wallet withdrawal tx. Body: \`{ userPubkey, tokenMint, amount? }\` — omit \`amount\` for max. | | \`POST /withdraw/confirm\` | Submit the signed tx. Body: \`{ signedTransaction }\`. | ## Order-level withdrawals | Endpoint | What it does | | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------ | | \`POST /orders/{orderId}/withdraw\` | Build a withdrawal tx for one terminal order. Returns \`withdrawalAmounts\` (per-mint preview). Safe to retry. | | \`POST /orders/{orderId}/withdraw/confirm\` | Submit the signed tx. | | \`POST /orders/{orderId}/withdraw/abandon\` | Release a stuck \`withdrawalStatus: pending\` lock. Idempotent. | Both withdrawal flows are walked through in \[Withdrawals\](/titan/developer-doc/dca-partner-api/guides/withdrawals.md). ## Partner reporting Server-to-server, \`X-Titan-Key\` only — no \`X-Titan-User\`. Rows are scoped to your tenant and capped at 500. Narrow to one user with \`?userId=\` (the opaque Titan id, not your \`sub\`). ### \`GET /partners/me/orders\` Query params: \`status\` (comma-separated; allowed subset: \`active\`, \`paused\`, \`completed\`, \`failed\`, \`cancelled\`), \`orderType\` (\`dca\`), \`userId\`, \`createdAtGte\`, \`createdAtLte\` (ISO-8601 — an invalid timestamp returns \`400 VALIDATION\_ERROR\`). {% hint style="info" %} The \`status\` filter here is a deliberate subset — \`executing\`, \`pending\`, \`pending\_modification\`, and \`expired\` can't be filtered on this endpoint. Orders in those states still appear in unfiltered responses, just not when \`status=\` is set. {% endhint %} Each row is the same shape as \`GET /dca/{orderId}\` \*\*except\*\* \`originatingPartner\` and the internal \`tenantId\` are omitted — every row here is yours by definition. ### \`GET /partners/me/orders/{id}\` A single order by id, scoped to your tenant. \`404\` if it doesn't exist \*\*or\*\* belongs to another partner (no cross-tenant existence leak). ### \`GET /partners/me/executions\` Per-execution rows including the fee snapshot, for billing reconciliation. Query params: \`userId\`, \`createdAtGte\`, \`createdAtLte\`. Row shape and fixed-point rules are in \[Order & Execution Schema\](/titan/developer-doc/dca-partner-api/reference/schema.md#execution). ## Related pages \* \[Order & Execution Schema\](/titan/developer-doc/dca-partner-api/reference/schema.md) — every field on an order, execution, and balance row \* \[Error Codes\](/titan/developer-doc/dca-partner-api/reference/error-codes.md) — the complete catalog, grouped by category \* \[Limits & Idempotency\](/titan/developer-doc/dca-partner-api/reference/limits.md) — row caps, TTLs, and the idempotency contract --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct.md). # Titan Direct \*\*Built for traders who can't afford stale quotes.\*\* Titan Direct is a persistent WebSocket connection that streams live swap quotes as on-chain state changes. All messages are binary frames encoded with \[MessagePack\](/titan/developer-doc/swap-api/reference/wire-protocol.md). --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/schema.md). # Order & Execution Schema The objects the DCA Partner API returns. Amounts are integer strings in the token's smallest unit; timestamps are ISO-8601 unless noted as Unix seconds. Treat all ids as opaque. ## Order Returned by \`GET /dca/{orderId}\`, \`POST /orders/confirm\`, and the order lists. \`\`\`json { "id": "9b3f1ad0-7c34-4e1f-bcfb-1c9a5a3a7b21", "tenantId": "", "originatingPartner": { "id": "your-partner-id", "name": "Your Brand" }, "userId": "", "walletAddress": "GZk2v…", "outputRecipientAddress": "GZk2v…", "orderType": "dca", "status": "active", "previousStatus": null, "inputMint": "So111…", "outputMint": "EPjFW…", "totalAmount": "1000000000", "amountPerCycle": "100000000", "cycleFrequencySeconds": 86400, "minOutputPerCycle": null, "maxOutputPerCycle": null, "cyclesCompleted": 3, "totalCycles": 10, "amountSpent": "300000000", "amountReceived": "150000000", "startAt": null, "expiresAt": null, "nextExecutionAt": "2025-01-04T00:00:00.000Z", "lastExecutionAt": "2025-01-03T00:00:00.000Z", "lastExecutionTxHash": "", "createdAt": "2025-01-01T00:00:00.000Z", "updatedAt": "2025-01-01T00:00:00.000Z", "cancelledAt": null, "completedAt": null, "failedAt": null, "failureReason": null, "withdrawalStatus": "none", "withdrawalTxHash": null, "withdrawalRequestedAt": null, "withdrawalCompletedAt": null, "platformFeeBpsOverride": 50 } \`\`\` | Field | Meaning | | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \`tenantId\` | Your tenant uuid. Present on user-scoped reads; \*\*omitted\*\* on \`/partners/me/\*\` reporting reads. | | \`originatingPartner\` | Resolved partner attribution. Present on user-scoped reads; \*\*omitted\*\* on \`/partners/me/\*\` reads. | | \`userId\` | The opaque Titan user id (same value \`GET /me\` returns). | | \`walletAddress\` | The user's \*\*manager\*\* (Titan-managed Solana pubkey). | | \`outputRecipientAddress\` | Where each cycle's output lands — the manager (\`walletAddress\`) or the user's external \`userPubkey\`. Immutable after create. | | \`status\` | \`pending\` \\| \`active\` \\| \`executing\` \\| \`pending\_modification\` \\| \`paused\` \\| \`completed\` \\| \`cancelled\` \\| \`failed\` \\| \`expired\`. | | \`previousStatus\` | The resting state to render while an order passes through \`executing\` / \`pending\_modification\`. \`null\` otherwise. | | \`cyclesCompleted\` / \`totalCycles\` | Progress counters. | | \`amountSpent\` / \`amountReceived\` | Cumulative input spent and output received across all cycles. | | \`nextExecutionAt\` / \`lastExecutionAt\` | \`null\` before the first cycle / after a terminal state. | | \`lastExecutionTxHash\` | Solana signature of the most recent successful cycle, or \`null\`. | | \`failureReason\` | Populated when \`status = failed\`. Short label suitable for surfacing. | | \`withdrawalStatus\` | \`none\` \\| \`pending\` \\| \`completed\`. Independent of \`status\`. | | \`withdrawalTxHash\` | Signature of the fund return once \`withdrawalStatus = completed\`, or \`null\` (including when a failed order's auto-return found nothing to send). Always a real signature or \`null\` — never a placeholder. | | \`withdrawalRequestedAt\` / \`withdrawalCompletedAt\` | Track the withdrawal lifecycle in step with \`withdrawalStatus\`. | | \`platformFeeBpsOverride\` | The per-order fee override, if one was set. | {% hint style="info" %} Rely only on the fields documented here. A response may include additional fields not listed above — treat them as internal and subject to change without notice. {% endhint %} ## Execution Returned by \`GET /orders/{orderId}/executions\` and \`GET /partners/me/executions\`. Capped at 500 rows, most recent first. \`\`\`json { "id": "c12a8b6f-2f5a-4e26-9c1b-8a4f9e7d1b22", "orderId": "9b3f1ad0-…", "executionType": "dca\_cycle", "status": "success", "inputAmount": "100000000", "outputAmount": "50000000", "outputAmountUsd": "5000000", "outputAmountUsdDecimals": 6, "price": "500000", "priceDecimals": 6, "txSignature": "", "failureReason": null, "executedAt": "2025-01-02T00:00:00.000Z", "platformFeeWallet": "FeEa…", "platformFeeBps": 50, "platformFeeMint": "EPjFW…", "platformFeeAmount": "25000" } \`\`\` | Field | Notes | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \`executionType\` | \`dca\_cycle\`. | | \`status\` | \`pending\` (in flight), \`success\` (confirmed on-chain), \`failed\` (terminal for that cycle). | | \`price\` | Integer string — a fixed-point number scaled by \`priceDecimals\`. \`price = "500000"\` with \`priceDecimals = 6\` means 0.5 output per input. Don't parse it as a float. | | \`outputAmountUsd\` | Same fixed-point convention with \`outputAmountUsdDecimals\`. May be \`null\` if pricing was unavailable at execution time. | | \`failureReason\` | Short error label when \`status = failed\`; \`null\` otherwise. (The on-the-wire field is \`failureReason\`, not \`errorMessage\`.) | | \`platformFee\*\` | Snapshot of the fee taken on this cycle. All four populate together when a fee was charged; all four are \`null\` when none was (zero \`bps\`, or no tenant fee wallet). | ## Balance row Returned by \`GET /me/balance\` inside \`data.balances\[\]\`. | Field | Meaning | | --------------------- | ----------------------------------------------------------------------------------------------------------- | | \`totalBalance\` | Raw on-chain balance of the mint in the manager. | | \`lockedForFutureTxns\` | Reserved by active DCA orders (\`totalAmount − amountSpent\` of the input mint). Output mints are not locked. | | \`withdrawalPending\` | Reserved by an in-flight order-level withdrawal. | | \`availableToWithdraw\` | \`max(total − locked − withdrawalPending, 0)\`. Source of truth for "withdraw max". | | \`programId\` | One of \`native\`, \`token\`, \`token-2022\`. | | \`symbol\` | Resolved for \`SOL\`, \`USDC\`, \`USDT\`; otherwise \`null\` (look it up in your token registry). | ## Conventions Amounts are integer strings in the smallest unit, to avoid JS precision loss. Order, pending-order, and execution ids are UUIDv4; the Titan \`userId\` is an opaque string. Both legacy SPL Token and Token-2022 mints are supported, detected per mint — though Token-2022 mints with transfer hooks, transfer fees, or unusual extensions may fail to swap or transfer, and the API surfaces an explicit error when they do. ## Related pages \* \[Lifecycle & Polling\](/titan/developer-doc/dca-partner-api/guides/lifecycle.md) — how \`status\` and \`withdrawalStatus\` transition \* \[Platform Fees\](/titan/developer-doc/dca-partner-api/guides/platform-fees.md) — how the execution fee snapshot is produced \* \[Endpoints\](/titan/developer-doc/dca-partner-api/reference/endpoints.md) — which routes return each object --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/gateway.md). # Titan Gateway \*\*The fastest path from zero to production-grade swap execution on Solana.\*\* Titan Gateway exposes the same Argos routing engine as Titan Direct through simple REST endpoints. All responses are \[MessagePack\](/titan/developer-doc/swap-api/reference/wire-protocol.md)-encoded. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/orders.md). # Create & Manage Orders A DCA order spends a fixed \`amountPerCycle\` of the input mint on a schedule until \`totalAmount\` is exhausted. Creating one is always two steps, because the input is funded from the user's external wallet and only the user can sign that deposit. Once the order is active, Titan runs each cycle on its own. ## Create — intent then confirm \`POST /orders/intent\` returns an unsigned deposit transaction. Nothing moves until the user signs it and you confirm. \`\`\`typescript const intent = await callTitanDca('/orders/intent', { method: 'POST', sub, body: { orderType: 'dca', userPubkey, onboardIfNeeded: true, // optional: must be exactly true to provision a brand-new user inline platformFee: { bps: 50 }, // optional per-order override; omit for your tenant default config: { inputMint: 'So11111111111111111111111111111111111111112', outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', totalAmount: '1000000000', amountPerCycle: '100000000', cycleFrequencySeconds: 86400, minOutputPerCycle: '0', // optional: abort a cycle if quote is below this maxOutputPerCycle: '0', // optional: abort a cycle if quote is above this startAt: 1735689600, // optional: Unix seconds; first cycle waits until then expiresAt: 1767225600, // optional: Unix seconds; order auto-expires }, }, }); const { pendingOrderId, transaction, expiresAt } = intent.data; \`\`\` \`userPubkey\` must be the wallet the user attested at onboarding — anything else returns \`403 VALIDATION\_ERROR\`. The response \`transaction\` is base64 and unsigned; the user has until \`expiresAt\` (5 minutes) to sign it. {% hint style="info" %} To onboard a brand-new user and create their first order in a single signature, pass \`onboardIfNeeded: true\` here — Titan provisions the manager inline if the \`X-Titan-User\` id was never onboarded. This path requires a \`409 USER\_PUBKEY\_CONFLICT\` fallback to the two-step SIWS flow. See \[Single-transaction onboarding\](/titan/developer-doc/dca-partner-api/guides/onboarding.md#single-transaction-onboarding). {% endhint %} {% hint style="warning" %} \`amountPerCycle\` must be worth at least \*\*$10 USD\*\* per cycle by default (your tenant's configured floor). USDC and USDT count as $1.00; other mints are priced by the oracle at request time. Below the floor returns \`400 MIN\_NOTIONAL\_NOT\_MET\`. {% endhint %} Then confirm with the signed transaction: \`\`\`typescript const confirmed = await callTitanDca('/orders/confirm', { method: 'POST', sub, body: { pendingOrderId, signedTransaction }, }); const { order, txSignature } = confirmed.data; // order.status === "active" \`\`\` {% hint style="info" %} Send an \`X-Idempotency-Key\` on \`intent\` and \`confirm\` if you intend to retry them. Use a deterministic key per logical action — e.g. \`dca:create::v1\` — so a network retry hashes to the same key instead of creating a second order. See \[Limits & Idempotency\](/titan/developer-doc/dca-partner-api/reference/limits.md#idempotency). {% endhint %} ### Where swap output lands By default each cycle's output goes to the user's external wallet (\`userPubkey\`). To keep it inside the manager instead — useful if the user is accumulating before a single withdrawal — set \`outputRecipientAddress\` to the manager address (\`walletAddress\` from onboarding). It must be either the \`userPubkey\` or the manager, and it's immutable once the order is confirmed. ## Read order state | Endpoint | Returns | | ---------------------------------- | --------------------------------------------------------------------------------------------- | | \`GET /me/orders?status=&type=\` | All of the user's orders, optionally filtered. | | \`GET /me/orders/active\` | Currently running orders. | | \`GET /me/orders/history\` | Terminal orders (\`completed\` / \`cancelled\` / \`failed\` / \`expired\`). | | \`GET /dca/{orderId}\` | One order with full progress — \`cyclesCompleted\`, \`amountSpent\`, \`nextExecutionAt\`, and more. | | \`GET /orders/{orderId}/executions\` | Per-cycle execution history, most recent first. | See \[Order & Execution Schema\](/titan/developer-doc/dca-partner-api/reference/schema.md) for every field. ## Modify \`PATCH /dca/{orderId}\` edits an \`active\` or \`paused\` order. Editable fields: \`amountPerCycle\`, \`cycleFrequencySeconds\`, \`totalCycles\`, \`totalAmount\`, \`minOutputPerCycle\`, \`maxOutputPerCycle\`. Most edits apply immediately and return the updated order: \`\`\`json { "success": true, "data": { "requiresTransaction": false, "order": { "...": "…" } } } \`\`\` Changing \`totalAmount\` is different: it has to deposit or withdraw the difference, so it becomes a two-step flow like creation. The response carries \`requiresTransaction: true\` with an unsigned \`transaction\`: \`\`\`json { "success": true, "data": { "requiresTransaction": true, "transactionType": "deposit", "transactionAmount": "1000000000", "newTotalAmount": "2000000000", "transaction": "", "cyclesCompleted": 3, "config": { "...": "echoed config you submitted" } } } \`\`\` Have the user sign it, then submit to \`POST /dca/{orderId}/modify/confirm\` with the same \`cyclesCompleted\` and \`config\` you got back. {% hint style="warning" %} DCA executions for the order pause while a modification is being signed — your signed \`totalAmount\` can't be applied to a different on-chain state than the user reviewed. The lock auto-releases after \*\*30 seconds\*\*; if it expires before confirm, you get \`409 LOCK\_EXPIRED\` and re-run the modify intent. If a cycle lands mid-signature, \`cyclesCompleted\` won't match and confirm returns \`400 MODIFICATION\_ERROR\` — re-fetch and let the user re-confirm. {% endhint %} ## Pause, resume, retry \`POST /orders/{orderId}/pause\` stops an \`active\` order; \`POST /orders/{orderId}/resume\` restarts a \`paused\` one. Each returns the updated order, or \`400 INVALID\_STATE\` if the order isn't in the right state or a withdrawal is in flight. \`POST /orders/{orderId}/retry\` flips a \`failed\` order back to \`active\`. There's a catch worth knowing: {% hint style="danger" %} A failed order's unspent input is \*\*auto-returned\*\* to the user's external wallet shortly after it fails. Retry only succeeds in the brief window before that return runs. Once the return is in progress or done, retry returns \`WITHDRAWAL\_IN\_PROGRESS\` or \`ALREADY\_WITHDRAWN\`, and the user has to create a new order. {% endhint %} ## Cancel \`POST /orders/{orderId}/cancel\` takes a discriminated body on the \`withdraw\` flag: \`\`\`typescript // Cancel only — no transaction built. await callTitanDca(\`/orders/${orderId}/cancel\`, { method: 'POST', sub, body: { withdraw: false } }); // Cancel and get an unsigned multi-token withdrawal tx back. await callTitanDca(\`/orders/${orderId}/cancel\`, { method: 'POST', sub, body: { withdraw: true, userPubkey }, }); \`\`\` {% hint style="warning" %} If you omit the body entirely, \`withdraw\` defaults to \`true\` — which still requires \`userPubkey\`. An empty body therefore fails with \`400 VALIDATION\_ERROR\`. Always send one of the two shapes above explicitly. {% endhint %} With \`withdraw: true\`, the response includes a \`transaction\` and a \`withdrawalAmounts\` array (per-mint amounts the tx will move). The user signs it, then you submit to \`POST /orders/{orderId}/withdraw/confirm\`. If there's nothing to withdraw, the response omits \`transaction\` and sets a \`message\`. ## Errors The create and modify flows share most failure modes. The ones worth handling explicitly: | HTTP | \`error.code\` | When | | ---- | --------------------------------- | ------------------------------------------------------------------------------------------------------------ | | 400 | \`VALIDATION\_ERROR\` | Invalid body, address, or amount. \`details.issues\` lists each failure. | | 400 | \`INVALID\_MINTS\` | \`inputMint === outputMint\`. | | 400 | \`MIN\_NOTIONAL\_NOT\_MET\` | Per-cycle value below your tenant's minimum. \`details\` echoes \`mint\`, \`amount\`, \`usdCents\`, \`requiredCents\`. | | 400 | \`EXPIRED\` / \`TRANSACTION\_EXPIRED\` | The 5-minute window to sign and confirm elapsed. Request a new intent. | | 400 | \`TRANSACTION\_TAMPERED\` | The signed tx doesn't match the unsigned one Titan returned. | | 403 | \`VALIDATION\_ERROR\` | \`userPubkey\` isn't the wallet the user attested at onboarding. | | 409 | \`ONBOARDING\_INCOMPLETE\` | Manager not fully provisioned. Re-call \`POST /partner/onboard\`, then retry. | | 409 | \`LOCK\_FAILED\` / \`LOCK\_EXPIRED\` | Concurrent or timed-out modification. Re-run the modify intent. | | 422 | \`IDEMPOTENCY\_KEY\_REUSED\` | Same \`X-Idempotency-Key\` with a different body. | | 503 | \`PRICE\_ORACLE\_UNAVAILABLE\` | Couldn't price a non-stable input mint to enforce the minimum. Transient — retry shortly. | The full catalog, including confirm-step and withdrawal codes, is in \[Error Codes\](/titan/developer-doc/dca-partner-api/reference/error-codes.md). ## Related pages \* \[Withdrawals\](/titan/developer-doc/dca-partner-api/guides/withdrawals.md) — wallet-level and order-level fund returns \* \[Lifecycle & Polling\](/titan/developer-doc/dca-partner-api/guides/lifecycle.md) — order statuses and how to keep your UI in sync \* \[Order & Execution Schema\](/titan/developer-doc/dca-partner-api/reference/schema.md) — every field on an order and an execution --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/lifecycle.md). # Lifecycle & Polling An order moves through a fixed set of statuses from creation to a terminal state. Titan doesn't push these changes — there are no webhooks today — so you poll the read endpoints while a user is looking at their DCA surfaces. ## Order statuses \`\`\` intent ──▶ pending ──confirm──▶ active ──cycle..N──▶ completed │ ├─ pause/resume ↔ paused │ ├─ executing ── (during a cycle) │ ├─ pending\_modification ── (during modify signing) │ ├─ failed ── (retry budget exhausted) ── unspent input auto-returned │ (retry ──▶ active only in the brief pre-return window) │ ├─ cancelled ── (user cancel) │ └─ expired ── (config.expiresAt reached) \`\`\` The full enum on every order: \`pending | active | executing | pending\_modification | paused | completed | cancelled | failed | expired\`. \`executing\` and \`pending\_modification\` are transient states an order passes through during a cycle or a modify-signing window. The order's \`previousStatus\` holds the resting state it'll return to, so you can keep rendering "active" or "paused" instead of flickering. ## Automatic input return When an order permanently fails, Titan returns its remaining unspent input to the user's external wallet on its own — no partner action, no user signature. Funds reserved for the user's other active orders are untouched. This is why a failed order's \`withdrawalStatus\` transitions to \`completed\` without you doing anything. Don't build a manual "withdraw failed order" step — surface the return via \`withdrawalStatus\` and \`withdrawalTxHash\` instead. \`withdrawalStatus\` tracks the return of an order's funds: \`none → pending → completed\` (or back to \`none\` via abandon). It advances automatically for failed orders, and on demand when you call order-level withdraw for a \`completed\` or \`cancelled\` order. \`withdrawalTxHash\` holds the on-chain signature of the return — a real signature, or \`null\` when no transfer was needed. ## What to poll | Surface | Endpoint | | ----------------------------------------- | -------------------------------------------- | | Active-orders list (user's DCA dashboard) | \`GET /me/orders/active\` | | Single-order detail (user has it open) | \`GET /dca/{orderId}\` | | Execution history (during/after a cycle) | \`GET /orders/{orderId}/executions\` | | Back-office reconciliation | \`GET /partners/me/executions?createdAtGte=…\` | A few rules that keep polling cheap and correct: \*\*Don't poll during the two-step flows.\*\* Between \`intent\` → user signs → \`confirm\`, just wait for the signature and call \`confirm\`. Pending-order transitions are deterministic once \`confirm\` returns. \*\*Back off on \`EXECUTION\_IN\_FLIGHT\`.\*\* A \`409\` from cancel or withdraw means a swap is still being reconciled. Retry every few seconds; the server-side recovery loop settles it within a small bounded window. \*\*Don't double-poll the same user.\*\* If your UI has both a list and a detail view open, share state in your frontend rather than polling both endpoints for the same data. ## Related pages \* \[Create & Manage Orders\](/titan/developer-doc/dca-partner-api/guides/orders.md) — the state transitions you trigger \* \[Withdrawals\](/titan/developer-doc/dca-partner-api/guides/withdrawals.md) — how \`withdrawalStatus\` advances \* \[Order & Execution Schema\](/titan/developer-doc/dca-partner-api/reference/schema.md) — every status-related field --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct-vs-gateway.md). # Titan Direct vs Titan Gateway Two interfaces to the same execution infrastructure. Both route through Titan's advanced algorithms, return the same quote types, and produce identical quote quality and transaction output. \*\*The difference is interface and workflow, not routing quality.\*\* ## Titan Direct — WebSocket API Titan Direct is a WebSocket API built for users where latency and execution are critical to their operations. Market makers, prop trading desks, and quant/algo traders operate in an environment where quote freshness and fill latency are existential. A persistent WebSocket connection eliminates the overhead of repeated HTTP handshakes and allows Titan to push live quote updates without polling. Routes are served by Argos, Titan's proprietary routing engine — giving users the best available price across the full liquidity landscape. \*\*Titan Direct is the only WebSocket-native trading API on Solana. Built for traders who can't afford stale quotes.\*\* \*\*Use Direct when you need:\*\* \* Real-time streaming quotes that refresh automatically as on-chain state changes \* Persistent connections for trading bots, market making, or algorithmic strategies \* The lowest possible latency between quote and execution ## Titan Gateway — REST API Titan Gateway is a REST API built for developers and projects integrating swap functionality into products — wallets, aggregators, DeFi protocols, and consumer apps. These teams work in REST environments and do not need to redesign their architecture for a WebSocket connection. \*\*Titan Gateway is the fastest path from zero to production-grade swap execution on Solana.\*\* \*\*Gateway is not a simplified version of Direct.\*\* It is purpose-built for integration workflows, backed by the same Argos routing engine that powers Direct — \*\*the routing quality is identical.\*\* The differentiation is purely about interface and integration workflow. \*\*Use Gateway when you need:\*\* \* Standard REST endpoints that fit into existing backend infrastructure \* A single quote per user action — one-click swap buttons, price displays, or backend services \* Drop-in integration without WebSocket infrastructure ## Comparison | | Titan Direct | Titan Gateway | | -------------- | ------------------------------ | ----------------------------------- | | Interface | WebSocket | REST | | Quote delivery | Streaming — continuous updates | Per-request — one response per call | | Connection | Persistent | Stateless | ## API endpoint mapping Every Titan Direct RPC method has a corresponding Gateway REST endpoint. The request parameters and response types are the same. | Titan Gateway | Titan Direct | | ------------------------ | -------------------- | | \`GET /api/v1/info\` | \`GetInfo\` | | \`GET /api/v1/providers\` | \`ListProviders\` | | \`GET /api/v1/venues\` | \`GetVenues\` | | \`GET /api/v1/quote/swap\` | \`NewSwapQuoteStream\` | ## Which should I use? If you're not sure, start with Titan Direct. The \[\`@titanexchange/sdk-ts\`\](/titan/developer-doc/resources/sdk.md) SDK handles the WebSocket connection, MessagePack encoding, and compression negotiation for you. If your architecture requires REST or a persistent connection isn't practical, use Titan Gateway — you get the same routing quality through a familiar request/response pattern. ## Related pages \* \[Quickstart\](/titan/developer-doc/swap-api/quickstart.md) — first quote with both Direct and Gateway \* \[Stream & Execute a Swap\](/titan/developer-doc/swap-api/guides/stream-and-execute.md) — full Titan Direct guide \* \[Connection & Negotiation\](/titan/developer-doc/swap-api/reference/direct/connection.md) — WebSocket protocol details \* \[Wire Protocol\](/titan/developer-doc/swap-api/reference/wire-protocol.md) — MessagePack encoding and compression --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview.md). # Overview \*\*Titan's Limit Orders are designed to thrive in a competitive environment where searchers play a central role.\*\* By participating as a searcher, you gain access to a marketplace of on-chain limit orders. Limit orders are resting on-chain and can be partially or completely filled. \*\*Fees are charged to takers\*\* and are charged as \`output\_mint\` tokens. \*\*Program address:\*\* \`TitanLozLMhczcwrioEguG2aAmiATAPXdYpBg3DbeKK\` \*\*\* ## Order structure Each limit order is a PDA derived from the maker's public key, input mint, output mint, and an order ID. \`\`\`rust use pinocchio::pubkey::{create\_program\_address, Pubkey}; use bytemuck::{Pod, Zeroable}; /// Limit order structure #\[repr(C)\] #\[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)\] pub struct LimitOrder { // The public key of the order, pub maker: Pubkey, // Input mint of the limit order pub input\_mint: Pubkey, // Output mint of the limit order pub output\_mint: Pubkey, // Slot which the order was created pub creation\_slot: u64, // The slot at which the order expires pub expiration\_slot: u64, // The amount of input tokens to be exchanged pub amount: u64, // The amount of input tokens that have been filled pub amount\_filled: u64, // The amount of output tokens that have been exchanged. pub out\_amount\_filled: u64, // The amount of output tokens that the maker has withdrawn. pub out\_amount\_withdrawn: u64, // The amount of fees paid in the smallest unit of from\_token mint. pub fees\_paid: u64, // Price base in the order, in the smallest unit of output token pub price\_base: u64, // Price exponent, price is calculated as price\_base \* 10^(-price\_exponent) pub price\_exponent: u8, // The status of the order pub status: u8, // Bump seed for the limit order PDA pub bump: u8, // Unique identifier for the order, used to differentiate orders for same // (owner, input\_mint, output\_mint) tuple pub id: u8, // Bump seed for the input mint vault PDA pub input\_mint\_vault\_bump: u8, // Bump seed for the output mint vault PDA pub output\_mint\_vault\_bump: u8, // Time in order pub time\_in\_force: u8, // Fees ticks rate for the order from takers. pub fee\_ticks: u8, } \`\`\` \*\*PDA seeds:\*\* \`\["order", maker, input\_mint, output\_mint, id, bump\]\` \*\*Account size:\*\* 168 bytes. ### PDA derivation \`\`\`rust impl LimitOrder { pub const SEEDS: &'static \[u8\] = b"order"; pub const LEN: usize = 168; /// Get the pda address for the limit order, given the maker, input mint, /// output mint, id and bump. pub fn get\_pda\_address( maker: &Pubkey, input\_mint: &Pubkey, output\_mint: &Pubkey, id: u8, bump: u8, ) -> Result { let b0 = &\[id\]; let b1 = &\[bump\]; let seeds\_with\_bump = \[\ LimitOrder::SEEDS,\ maker.as\_ref(),\ input\_mint.as\_ref(),\ output\_mint.as\_ref(),\ b0,\ b1,\ \] .to\_vec(); create\_program\_address(&seeds\_with\_bump, &crate::ID) } } \`\`\` \*\*\* ## Price calculation Price is stored as \`price\_base \* 10^(-price\_exponent)\`. For example, a 100 USDC → 1 SOL order uses \`price\_base = 1\` and \`price\_exponent = 2\`, giving a price of \`0.01\` output tokens per input token. \`\`\`rust impl LimitOrder { /// Calculate the costs and fees for a given amount and fee ticks. pub fn calculate\_costs\_and\_fee( &self, amount: u64, fee\_ticks: u8, ) -> Result<(u64, u64), ProgramError> { let amount\_u128 = amount as u128; let price\_base = self.price\_base as u128; let price\_exponent = 10u128.pow(self.price\_exponent as u32); let fee\_units\_u128 = (fee\_ticks as u16).saturating\_mul(FEE\_TICK\_UNITS as u16) as u128; // Calculate the transfer amount and fee amount. // Should never overflow, since its u64 \* u64 // Use method to perform ceiling math division: (a + b - 1) / b let cost\_u128 = amount\_u128 .saturating\_mul(price\_base) .checked\_add(price\_exponent.saturating\_sub(1)) .ok\_or(ProgramError::ArithmeticOverflow)? .saturating\_div(price\_exponent); let cost: u64 = cost\_u128 .try\_into() .map\_err(|\_| ProgramError::ArithmeticOverflow)?; let fees: u64 = cost\_u128 .saturating\_mul(fee\_units\_u128) .saturating\_div(FEE\_TICK\_DIVISOR) .try\_into() .map\_err(|\_| ProgramError::ArithmeticOverflow)?; Ok((cost, fees.max(1))) } /// Amount left to be filled in the order. pub fn get\_remaining\_amount(&self) -> u64 { self.amount.saturating\_sub(self.amount\_filled) } } \`\`\` \*\*\* ## Fees Fees are charged to takers in the \*\*output token\*\*. The fee rate is stored as \`fee\_ticks\` on the order. \`\`\` fee\_units = fee\_ticks × 25 fee = cost × fee\_units / 1,000,000 \`\`\` \*\*The minimum fee is always 1 unit of the output token.\*\* \`\`\`rust /// Fee tick units pub const FEE\_TICK\_UNITS: u8 = 25; /// 1e6 units = 0.0001, used to convert fee tick rate to fee basis points pub const FEE\_TICK\_DIVISOR: u128 = 1\_000\_000; \`\`\` \*\*Fee receiver address:\*\* \`Bq5ZzfiU3vTiJPrBJFcr98BnUy9Wc1dg9ASeycB2tX1C\` \*\*\* ## Time-in-force Each order carries a time-in-force policy that controls fill behavior. \`\`\`rust /// Time in Force (TIF) for limit orders. Provides different behaviors for /// how long an order remains active and how it can be filled. #\[repr(u8)\] #\[derive(Clone, Copy, Debug, PartialEq)\] pub enum TimeInForce { /// Order is good until cancelled. Partial takes are allowed. GoodTillCancelled = 0, /// After taking any amount, order is closed. TakeCancelsOrder = 1, /// Takes must completely fill the order. AllOrNothing = 2, /// Same as TakeCancelsOrder but it must be filled at the same time of creation. ImmediateOrCancel = 3, /// Same as AllOrNothing but it must be filled at the same time of creation. FillOrKill = 4, } \`\`\` \* \*\*\`GoodTillCancelled\` (0)\*\* — Remains open until fully filled or cancelled. \*\*Partial fills allowed.\*\* \* \*\*\`TakeCancelsOrder\` (1)\*\* — Closes after any take, regardless of fill amount. \* \*\*\`AllOrNothing\` (2)\*\* — Takes must completely fill the remaining amount. \* \*\*\`ImmediateOrCancel\` (3)\*\* — Same as \`TakeCancelsOrder\`, but \*\*must be filled in the same slot as creation.\*\* \* \*\*\`FillOrKill\` (4)\*\* — Same as \`AllOrNothing\`, but \*\*must be filled in the same slot as creation.\*\* \*\*\* ## Order status \`\`\`rust /// Order status for limit orders. Indicates the current state of the order /// and how it can be interacted with. #\[repr(u8)\] #\[derive(Clone, Copy, Debug, PartialEq)\] pub enum OrderStatus { /// Order is open, can be partially filled, filled, cancelled Open = 0, /// Order is partially filled, can be filled or cancelled PartiallyFilled = 1, /// Order is filled, terminates, used for event logging Filled = 2, /// Order is cancelled, terminates, used for event logging Cancelled = 3, } \`\`\` \* \*\*\`Open\` (0)\*\* — Can be partially filled, fully filled, or cancelled. \* \*\*\`PartiallyFilled\` (1)\*\* — Some amount filled. Can still be filled or cancelled. \* \*\*\`Filled\` (2)\*\* — Fully filled. \*\*Terminal state.\*\* \* \*\*\`Cancelled\` (3)\*\* — Cancelled by maker. \*\*Terminal state.\*\* \*\*\* ## Related pages \* \[Placing Taker Orders\](https://github.com/Titan-Pathfinder/titan-gitbook-doc/blob/main/limit-orders/take-order.md) — TakeOrder instruction, accounts, WSOL handling, and full execution code \* \[Limit Order Events\](https://github.com/Titan-Pathfinder/titan-gitbook-doc/blob/main/limit-orders/events.md) — Event structure and parsing from program logs \* \[Error Codes\](https://github.com/Titan-Pathfinder/titan-gitbook-doc/blob/main/limit-orders/error-codes.md) — Program error codes --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/overview.md). # Overview Titan DCA lets you offer Dollar-Cost-Averaging on Solana without holding user keys, scheduling cycles, or building swap infrastructure. Your backend calls Titan; Titan provisions a managed wallet per user, runs each DCA cycle on schedule, and returns swap output to the user's own wallet. The integration is \*\*server-to-server\*\*. There's no SDK in your frontend and no new login UI. Your users keep authenticating however they already do; your backend authenticates to Titan with one API key and identifies each user by the same stable id you already use for them. ## How the pieces fit Your backend holds a partner \*\*API key\*\* (\`X-Titan-Key\`) and sends it on every request. For anything user-specific, it also sends \`X-Titan-User\` — your own id for that user. Titan validates the key, attributes the request to your tenant, resolves the user, and executes the order on a Titan-managed Solana wallet that belongs to that user. \`\`\` your backend ──(X-Titan-Key + X-Titan-User)──▶ Titan DCA ──▶ user's manager (policy-bound) │ └─ once per user: POST /partner/onboard (X-Titan-Key + user's SIWS) ──▶ provisions the manager \`\`\` The only cryptographic proof in the whole flow is a one-time \*\*Sign-In-with-Solana (SIWS)\*\* signature the user makes at onboarding. There's no OAuth, no token exchange, and no partner JWT. ## Core concepts | Concept | What it means for you | | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \*\*Tenant\*\* | Your partner account inside Titan DCA. Every order, execution, and row is tagged with your tenant id, so your data is isolated from other partners. | | \*\*Partner API key\*\* (\`X-Titan-Key\`) | A server-side secret Titan issues you. Send it on every request. Never ship it to a browser. | | \*\*Partner user id\*\* (\`X-Titan-User\`) | Your own stable id for an end-user. You supply it as \`sub\` at onboarding and as \`X-Titan-User\` on every user-scoped call. It's namespaced to your tenant, so two partners can use the same id without collision. | | \*\*Manager\*\* | A Solana wallet created and managed by Titan but owned by the user. Titan signs only what the user's policy permits — DCA swaps, and withdrawals back to the user's own external wallet. | | \*\*External wallet\*\* (\`userPubkey\`) | The user's existing Solana wallet. It pays network fees on user-signed transactions and, by default, receives swap output. The user proves ownership of it once, at onboarding, via SIWS. | | \*\*Two-step transactions\*\* | Anything that changes on-chain state happens in two calls: \*\*intent\*\* (Titan returns an unsigned tx) → user signs → \*\*confirm\*\* (you send the signed tx back; Titan co-signs and broadcasts). | | \*\*Automatic input return\*\* | When an order permanently fails, Titan returns its remaining unspent input to the user's external wallet — no partner action, no user signature. Funds reserved for the user's other active orders are left untouched. | Managers are global per end-user. If the same user shows up at another integrator with the same external wallet, they land in the \*\*same\*\* Titan-managed manager — balances and active orders follow the user. ## Environments | Environment | Base URL | Notes | | ----------- | ------------------------------------------- | --------------------- | | Production | \`https://api.chronos.titan.exchange/api/v1\` | Issued at onboarding. | A separate \*\*development\*\* base URL is provided at onboarding for end-to-end testing before launch. A key is bound to exactly one environment. Every endpoint path in this section is relative to the base URL. ## Where to go next The \[Quickstart\](/titan/developer-doc/dca-partner-api/quickstart.md) takes you from onboarding a user to creating your first order. From there, the guides cover each part of an integration — \[Onboarding\](/titan/developer-doc/dca-partner-api/guides/onboarding.md), \[Create & Manage Orders\](/titan/developer-doc/dca-partner-api/guides/orders.md), \[Withdrawals\](/titan/developer-doc/dca-partner-api/guides/withdrawals.md), \[Platform Fees\](/titan/developer-doc/dca-partner-api/guides/platform-fees.md), and \[Lifecycle & Polling\](/titan/developer-doc/dca-partner-api/guides/lifecycle.md). For the precise contract, see \[Authentication\](/titan/developer-doc/dca-partner-api/reference/authentication.md) and the full \[Endpoints\](/titan/developer-doc/dca-partner-api/reference/endpoints.md) reference. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference.md). # API Reference \*\*Full reference for every RPC method, endpoint, type, and error code across both Titan interfaces.\*\* Choose \[Titan Direct\](/titan/developer-doc/swap-api/reference/direct-vs-gateway.md) for real-time WebSocket streams or \[Titan Gateway\](/titan/developer-doc/swap-api/reference/direct-vs-gateway.md) for simple REST calls — both are backed by the same Argos routing engine. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/error-codes.md). # Error Codes \*\*These are the custom error codes thrown by the Titan Limit Order program.\*\* Each maps to a specific on-chain validation failure. \`\`\`rust #\[repr(u8)\] #\[derive(Debug, Clone, Copy, PartialEq, Eq)\] pub enum LimitOrderError { InvalidOrderStatus, // 0 InvalidAmount, // 1 InvalidMaker, // 2 InvalidMint, // 3 InvalidPrice, // 4 InvalidTokenAccountAuthority, // 5 InvalidTokenProgramId, // 6 InvalidAssociatedTokenAccountAddress, // 7 InvalidExtension, // 8 EventDeserializationError, // 9 ExpirationSlotExceeded, // 10 ExecutionTimeInForceViolation, // 11 MaxCostLimitExceeded, // 12 OrderNotExpired, // 13 } \`\`\` \* \*\*\`0\` — \`InvalidOrderStatus\`\*\* — Order is not in a valid state for this operation. Check the order's \[status\](/titan/developer-doc/searchers-limit-orders/overview.md#order-status) before interacting. \* \*\*\`1\` — \`InvalidAmount\`\*\* — Amount is zero or exceeds the remaining balance. \* \*\*\`2\` — \`InvalidMaker\`\*\* — Maker public key does not match the order. \* \*\*\`3\` — \`InvalidMint\`\*\* — Token mint does not match the order's input or output mint. \* \*\*\`4\` — \`InvalidPrice\`\*\* — Price parameters are invalid (e.g. zero \`price\_base\`). \* \*\*\`5\` — \`InvalidTokenAccountAuthority\`\*\* — Token account authority does not match the expected owner. \* \*\*\`6\` — \`InvalidTokenProgramId\`\*\* — Wrong token program passed for the mint. \*\*Use SPL Token for SPL mints, SPL Token-2022 for Token-2022 mints.\*\* \* \*\*\`7\` — \`InvalidAssociatedTokenAccountAddress\`\*\* — ATA address does not match the expected derivation. \* \*\*\`8\` — \`InvalidExtension\`\*\* — Token extension is not supported by the program. \* \*\*\`9\` — \`EventDeserializationError\`\*\* — Failed to deserialize event data. \* \*\*\`10\` — \`ExpirationSlotExceeded\`\*\* — Order has expired. \*\*Cannot be filled after the \`expiration\_slot\`.\*\* \* \*\*\`11\` — \`ExecutionTimeInForceViolation\`\*\* — Take violates the order's \[time-in-force\](/titan/developer-doc/searchers-limit-orders/overview.md#time-in-force) policy. For example, attempting a partial fill on an \`AllOrNothing\` order. \* \*\*\`12\` — \`MaxCostLimitExceeded\`\*\* — Cost exceeds the taker's \`max\_cost\_amount\`. Increase the limit or reduce the take amount. \* \*\*\`13\` — \`OrderNotExpired\`\*\* — Attempted to close an order that has not yet expired. Wait until \`expiration\_slot\` is reached. \*\*\* ## Related pages \* \[Limit Orders Overview\](/titan/developer-doc/searchers-limit-orders/overview.md) — Order structure, time-in-force, order status \* \[Placing Taker Orders\](/titan/developer-doc/searchers-limit-orders/take-order.md) — TakeOrder instruction and execution code \* \[Limit Order Events\](/titan/developer-doc/searchers-limit-orders/events.md) — Event structure and parsing from program logs --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference.md). # API Reference \*\*The precise contract for every route, object, and error code in the DCA Partner API.\*\* Start with \[Authentication\](/titan/developer-doc/dca-partner-api/reference/authentication.md) for the header model, then \[Endpoints\](/titan/developer-doc/dca-partner-api/reference/endpoints.md) for the routes. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides.md). # Guides \*\*Practical walkthroughs for each part of a DCA integration, in the order you'll build them.\*\* Each guide builds on the \[Quickstart\](/titan/developer-doc/dca-partner-api/quickstart.md) and assumes you've onboarded at least one user. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/events.md). # Limit Order Events \*\*You can listen to limit order events by parsing through the program logs for emitted data.\*\* Events are Borsh-serialized and emitted in program logs for every order lifecycle operation. \*\*\* ## Event structure \`\`\`rust #\[repr(C)\] #\[derive(Clone, Copy, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize)\] pub struct LimitOrderEvent { /// Event discriminator, used to identify the event type /// Create - 1, Take - 2, Cancel - 3, Modify - 4, Withdraw - 5, CloseExpired - 6 pub discriminator: u8, /// Instruction that triggered the event pub instruction: u8, /// The status of the order pub status: u8, /// Unique identifier for the order, used to differentiate orders for same /// (owner, input\_mint, output\_mint) tuple pub id: u8, /// The public key of the order pub maker: Pubkey, /// The mint of the input token pub input\_mint: Pubkey, /// The mint of the output token pub output\_mint: Pubkey, /// The slot at which the event was emitted pub slot: u64, /// The slot at which the order was created pub creation\_slot: u64, /// The slot at which the order expires pub expiration\_slot: u64, /// The amount of input tokens to be exchanged pub amount: u64, /// The amount of input tokens that have been filled pub amount\_filled: u64, /// The amount of output tokens that have been exchanged pub out\_amount\_filled: u64, /// The amount of fees paid in the smallest unit of from\_token mint pub fees\_paid: u64, /// Price base in the order, in the smallest unit of output token pub price\_base: u64, /// Price exponent, price is calculated as price\_base \* 10^(-price\_exponent) pub price\_exponent: u8, /// Time in order, used to determine how long the order is valid pub time\_in\_force: u8, /// Fee rate in ticks, used to determine the fee charged for the order pub fee\_ticks: u8, } \`\`\` \*\*\* ## Instruction discriminators The \`discriminator\` field is always \`0\`. The \`instruction\` field identifies which operation triggered the event: \* \*\*\`1\` — \`PlaceOrder\`\*\* — A new limit order was created. \* \*\*\`2\` — \`TakeOrder\`\*\* — A searcher filled (partially or fully) an order. \* \*\*\`3\` — \`CancelOrder\`\*\* — The maker cancelled the order. \* \*\*\`4\` — \`ModifyOrder\`\*\* — The maker modified the order parameters. \* \*\*\`5\` — \`WithdrawFilled\`\*\* — The maker withdrew filled output tokens from a partially filled order. \* \*\*\`6\` — \`CloseExpired\`\*\* — An expired order was closed and rent reclaimed. {% hint style="info" %} The \`status\` field in the event reflects the order's state \*\*after\*\* the operation. Use \[OrderStatus\](/titan/developer-doc/searchers-limit-orders/overview.md#order-status) values to interpret it. {% endhint %} \*\*\* ## Related pages \* \[Limit Orders Overview\](/titan/developer-doc/searchers-limit-orders/overview.md) — Order structure, time-in-force, order status, fees \* \[Placing Taker Orders\](/titan/developer-doc/searchers-limit-orders/take-order.md) — TakeOrder instruction and execution code \* \[Error Codes\](/titan/developer-doc/searchers-limit-orders/error-codes.md) — Program error codes --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/transaction-template.md). # Transaction Template When you build a swap transaction, you usually want to add instructions of your own — a compute-budget setting, a memo, a custom fee transfer, an oracle update, an app-specific log. The problem: every byte you add eats into the \*\*1232-byte Solana transaction limit\*\*, and the route Titan returned may no longer fit. \`transactionTemplate\` solves this. You tell Titan upfront what extra instructions, ALTs, and account metas will share the final transaction with the swap. Titan then sizes the route so the \*\*assembled\*\* transaction fits inside Solana's limits when you splice everything together. {% hint style="warning" %} \*\*\`transactionTemplate\` is incompatible with \`accountsLimitTotal\`, \`accountsLimitWritable\`, and \`sizeConstraint\`.\*\* The template \*\*is\*\* the sizing constraint — passing both returns an error. {% endhint %} ## When to use it \* You're prepending or appending instructions that aren't part of the swap (compute-budget settings, memos, app-specific logs, oracle pokes, custom fee transfers). \* You're using your own ALTs (rebate program, fee program, app-specific routing). \* You're hitting tx-size errors after combining the route with your own instructions. For everything else, the default sizing (\`accountsLimitTotal\` / \`accountsLimitWritable\`) is enough. ## Pair with V3 \*\*Use \`titanSwapVersion: 3\` whenever you use \`transactionTemplate\`.\*\* V3 manages input and output token accounts internally, so the router does \*\*not\*\* insert ATA create/close instructions around the swap — the full residual byte budget goes to the route. With V2, the router still adds wSOL wrap/unwrap and ATA-creation instructions, which makes templates less predictable. {% hint style="danger" %} \*\*\`titanSwapVersion\` is the integer \`3\`, not the string \`"V3"\`.\*\* Apollo rejects strings with \`Failed to deserialize query string: titanSwapVersion: invalid digit found in string\`. {% endhint %} ## The template See \[\`TransactionTemplate\`\](/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream.md#transaction-template) for the full struct. Three fields: \* \*\*\`i\`\*\* — Instructions that will sit in the final transaction \*\*before\*\* the swap. Include any ATA creation/deletion for the input and output mints yourself if you need them — the template doesn't assume the router will add them. (V3 doesn't need them; V2 might.) \* \*\*\`a\`\*\* — ALTs the surrounding transaction already references. \*\*Order matters\*\* — Solana resolves ALTs greedily. Provide them in the order you'll use when compiling the message. Titan extends this array with any ALTs it uses for the swap. \* \*\*\`m\`\*\* — Extra account metas that belong to the surrounding transaction but aren't reachable from any instruction in \`i\`. Rare — leave empty unless you need it. ### Wire format The MessagePack wire format uses \*\*single-letter field names\*\* for space efficiency. | Type | Wire field | Meaning | | --------------------------- | ---------- | ----------------------------------------------------------------- | | \`TransactionTemplate\` | \`i\` | \`instructions\` (array) | | \`TransactionTemplate\` | \`a\` | \`alts\` (array) | | \`TransactionTemplate\` | \`m\` | \`accountMetas\` (array) | | \`Instruction\` | \`p\` | \`programId\` (32-byte pubkey) | | \`Instruction\` | \`a\` | \`accounts\` (array of \`AccountMeta\`) | | \`Instruction\` | \`d\` | \`data\` (raw bytes) | | \`AccountMeta\` | \`p\` | \`pubkey\` (32-byte pubkey) | | \`AccountMeta\` | \`s\` | \`isSigner\` (bool) | | \`AccountMeta\` | \`w\` | \`isWritable\` (bool) | | \`AddressLookupTableAccount\` | \`p\` | \`key\` (32-byte ALT account address) | | \`AddressLookupTableAccount\` | \`a\` | \`addresses\` (array of 32-byte pubkeys \*inside\* the ALT, in order) | \*\*All pubkeys and instruction \`data\` are raw byte arrays, not Base58 or Base64 strings.\*\* ## REST vs WebSocket transport The template payload is the same shape on both transports, but the wrapping is different: \* \*\*REST (Gateway):\*\* MessagePack-encode the template, then \*\*Base64-encode the bytes\*\*, and pass as the \`transactionTemplate\` query-string value. \* \*\*WebSocket (Direct):\*\* Embed the template \*\*inline\*\* inside the \`swap\` object of your \`NewSwapQuoteStream\` request. The whole frame is MessagePack already — no Base64 wrapping. ## Example: V3 + compute-budget template A USDC → SOL swap on V3 with a minimal template containing only the two compute-budget instructions. This is the canonical real-world pattern. {% tabs %} {% tab title="Titan Gateway (REST)" %} \`\`\`typescript import { Encoder, decode } from '@msgpack/msgpack'; import bs58 from 'bs58'; import { Connection, MessageV0, PublicKey, TransactionInstruction, VersionedTransaction, } from '@solana/web3.js'; const encoder = new Encoder({ useBigInt64: true }); // --- Step 1: build the two compute-budget instructions --- const CB\_PROGRAM\_ID = new PublicKey('ComputeBudget111111111111111111111111111111'); function setComputeUnitLimitData(units: number): Uint8Array { // Discriminator 0x02 + u32 LE const data = new Uint8Array(5); data\[0\] = 0x02; new DataView(data.buffer).setUint32(1, units, true); return data; } function setComputeUnitPriceData(microLamports: bigint): Uint8Array { // Discriminator 0x03 + u64 LE const data = new Uint8Array(9); data\[0\] = 0x03; new DataView(data.buffer).setBigUint64(1, microLamports, true); return data; } // --- Step 2: assemble the TransactionTemplate using wire-format field names --- const template = { i: \[\ { p: CB\_PROGRAM\_ID.toBytes(), a: \[\], d: setComputeUnitLimitData(1\_400\_000) },\ { p: CB\_PROGRAM\_ID.toBytes(), a: \[\], d: setComputeUnitPriceData(0n) }, // sub real priority fee in prod\ \], a: \[\], m: \[\], }; // --- Step 3: MessagePack-encode, then Base64-encode --- const transactionTemplate = Buffer.from(encoder.encode(template)).toString('base64'); // --- Step 4: send the quote request --- const USDC = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'; const SOL = 'So11111111111111111111111111111111111111112'; const USER = 'Affd7LDkUY9fjWjjQSr9bvises1Cku4WSwdLNGBhgVW3'; const params = new URLSearchParams({ inputMint: USDC, outputMint: SOL, amount: '100000000', // 100 USDC userPublicKey: USER, slippageBps: '50', titanSwapVersion: '3', // integer 3, not "V3" simulate: 'false', transactionTemplate, }); const res = await fetch( \`${process.env.TITAN\_ENDPOINT}/api/v1/quote/swap?${params}\`, { headers: { 'Authorization': \`Bearer ${process.env.TITAN\_API\_KEY}\`, 'Accept': 'application/vnd.msgpack', }, } ); const quotes = decode(new Uint8Array(await res.arrayBuffer())) as any; const winner = quotes.metadata?.ExpectedWinner; const route = winner && quotes.quotes\[winner\]; // --- Step 5: assemble \[templateInstructions, ...routeInstructions\] --- function titanIxToTransactionIx(ix: any): TransactionInstruction { return new TransactionInstruction({ programId: new PublicKey(ix.p), data: Buffer.from(ix.d), keys: ix.a.map((a: any) => ({ pubkey: new PublicKey(a.p), isSigner: a.s, isWritable: a.w, })), }); } const templateIxs = template.i.map(titanIxToTransactionIx); const swapIxs = route.instructions.map(titanIxToTransactionIx); const allIxs = \[...templateIxs, ...swapIxs\]; // Compile into a v0 message with the route's ALTs, sign and send \`\`\` {% endtab %} {% tab title="Titan Direct (WebSocket)" %} On the WebSocket transport, the whole frame is already MessagePack — embed the template \*\*inline\*\* in the request body, no Base64 wrapping needed. \`\`\`typescript import WebSocket from 'ws'; import { Encoder, decode } from '@msgpack/msgpack'; import { PublicKey } from '@solana/web3.js'; const encoder = new Encoder({ useBigInt64: true }); const CB\_PROGRAM\_ID = new PublicKey('ComputeBudget111111111111111111111111111111'); function setComputeUnitLimitData(units: number) { const data = new Uint8Array(5); data\[0\] = 0x02; new DataView(data.buffer).setUint32(1, units, true); return data; } function setComputeUnitPriceData(microLamports: bigint) { const data = new Uint8Array(9); data\[0\] = 0x03; new DataView(data.buffer).setBigUint64(1, microLamports, true); return data; } const template = { i: \[\ { p: CB\_PROGRAM\_ID.toBytes(), a: \[\], d: setComputeUnitLimitData(1\_400\_000) },\ { p: CB\_PROGRAM\_ID.toBytes(), a: \[\], d: setComputeUnitPriceData(0n) },\ \], a: \[\], m: \[\], }; const ws = new WebSocket( \`${process.env.TITAN\_WS\_ENDPOINT}/api/v1/ws\`, \['v1.api.titan.ag'\], { headers: { Authorization: \`Bearer ${process.env.TITAN\_API\_KEY}\` } }, ); ws.on('open', () => { ws.send(encoder.encode({ id: 1, data: { NewSwapQuoteStream: { swap: { inputMint: new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v').toBytes(), outputMint: new PublicKey('So11111111111111111111111111111111111111112').toBytes(), amount: 100\_000\_000n, slippageBps: 50, providers: \['Metis', 'Titan'\], transactionTemplate: template, // inline, no Base64 }, transaction: { userPublicKey: new PublicKey('Affd7LDkUY9fjWjjQSr9bvises1Cku4WSwdLNGBhgVW3').toBytes(), titanSwapVersion: 3, // integer 3 }, update: { intervalMs: 60\_000, numQuotes: 1 }, }, }, })); }); ws.on('message', (raw) => { const msg = decode(raw as Uint8Array) as any; if ('StreamData' in msg && 'SwapQuotes' in msg.StreamData.payload) { const quotes = msg.StreamData.payload.SwapQuotes; const winner = quotes.metadata?.ExpectedWinner; const route = winner && quotes.quotes\[winner\]; // Assemble \[template.i, ...route.instructions\] as in the REST tab } }); \`\`\` {% endtab %} {% endtabs %} ## What changes in the route Without a template, Titan sizes routes assuming the swap is the only thing in the transaction — up to the server's defaults (currently 1168 bytes, 64 accounts). With a template, Titan \*\*subtracts the template's footprint\*\* from those budgets before choosing a route: \* A compute-budget template (14 bytes, 0 accounts) barely shifts routing — V3 routes usually return as a single instruction with the ALTs the router would have used anyway. \* A larger template (custom program calls, multiple ALTs, many account metas) pushes the router toward shorter routes — fewer hops, fewer venues — to leave room. Providers that can't fit a route within the remaining budget are silently dropped from the response. \* If no provider can fit a route, you get a 404 (Gateway) or an empty \`quotes\` map (Direct). ## Pitfalls \* \*\*Don't pair with \`accountsLimitTotal\` / \`accountsLimitWritable\` / \`sizeConstraint\`.\*\* The template replaces them. Passing both returns 400. \* \*\*\`titanSwapVersion\` is integer \`3\`, not string \`"V3"\`.\*\* Strings get rejected with \`invalid digit found in string\`. \* \*\*Use wire-format field names.\*\* Long-form names (\`programId\`, \`accounts\`, etc.) return \`400 Bad Request: missing field 'p'\`. \* \*\*Splice the template instructions BEFORE the route instructions\*\* when building the final v0 message. The template represents what the surrounding transaction looks like; the swap sits after it. \* \*\*ALT order is load-bearing.\*\* Solana resolves ALTs greedily; the first ALT containing an account wins. If your custom ALT and a Titan ALT both contain the same account, ordering decides which gets used. \* \*\*Encode binary as bytes, not Base58.\*\* Inside the MessagePack-encoded template, pubkeys and instruction \`data\` are raw bytes (msgpack \`bin\`) — don't pre-encode them to strings. ## Related pages \* \[NewSwapQuoteStream → Transaction Template\](/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream.md#transaction-template) — type definition \* \[NewSwapQuoteStream → Swap V3\](/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream.md#swap-v3) — V3 router details \* \[Quote Swap (Gateway)\](/titan/developer-doc/swap-api/reference/gateway/gateway-quote-swap.md) — Gateway swap reference \* \[Stream & Execute a Swap\](/titan/developer-doc/swap-api/guides/stream-and-execute.md) — full transaction-building walkthrough \* \[Configure Routing\](/titan/developer-doc/swap-api/guides/configure-routing.md) — \`accountsLimitTotal\`, \`accountsLimitWritable\`, and other size controls --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/resources/community-and-support.md). # Community & Support \*\*Connect with the Titan team and community for support, feedback, and updates.\*\* \*\*\* ## Discord Join the Titan Discord for real-time help, announcements, and discussion with other developers. {% hint style="info" %} \*\*Discord is the fastest way to get support.\*\* The team actively monitors developer channels. {% endhint %} \*\*\* ## GitHub \* \*\*TypeScript SDK\*\* — \[github.com/Titan-Pathfinder/titan-sdk-ts\](https://github.com/Titan-Pathfinder/titan-sdk-ts) \* \*\*Rust SDK crates\*\* — \[crates.io/search?q=titan-api-types\](https://crates.io/search?q=titan-api-types) \*\*Found a bug?\*\* Open an issue on the relevant SDK repository with reproduction steps. \*\*\* ## Getting API access See \[Get API Access\](/titan/developer-doc/getting-started/api-access.md) for instructions on obtaining your API key. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/fee-collection.md). # Fee Collection If you're building a product on top of Titan, you can collect a fee on every swap. Fees are deducted from the swap output (or input) and sent to a token account you control. ## How fees work The fee is taken from the \*\*output token\*\* by default. If you'd rather take the fee from the input side, set \`feeFromInputMint: true\`. Your fee account must be a token account for the correct mint — output mint by default, or input mint when \`feeFromInputMint\` is true. This account must already exist, or you must add the ATA creation instruction yourself. When fees are active, every \[\`SwapRoute\`\](/titan/developer-doc/swap-api/reference/types.md) in the quote response includes a \`platformFee\` field with the exact fee amount and rate. Show this to your users before they sign. These fields are part of \[\`TransactionParams\`\](/titan/developer-doc/swap-api/reference/types.md): \* \*\*\`feeAccount\`\*\* (\`Pubkey\`) — ATA to receive the fee. Must already exist on-chain, or you must add the ATA creation instruction yourself. \* \*\*\`feeBps\`\*\* (\`u16\`) — Fee rate in basis points (1 bps = 0.01%). If not specified, the default fee for your account is used. \* \*\*\`feeFromInputMint\`\*\* (\`bool\`) — If \`true\`, fee is taken from the input mint. Default \`false\`. ## Collect fees on output token (default) Create an ATA for the output mint before your first request, then pass \`feeAccount\` and \`feeBps\` in your transaction parameters. {% tabs %} {% tab title="Titan Direct" %} \`\`\`typescript import WebSocket from 'ws'; import { Encoder, Decoder } from '@msgpack/msgpack'; import bs58 from 'bs58'; import { zstdCompress, zstdDecompress } from 'http-encoding'; const encoder = new Encoder({ useBigInt64: true }); const decoder = new Decoder({ useBigInt64: true }); const url = \`${process.env.TITAN\_ENDPOINT}?auth=${process.env.TITAN\_API\_KEY}\`; const ws = new WebSocket(url, \[\ 'v1.api.titan.ag+zstd',\ 'v1.api.titan.ag',\ \]); const SOL = bs58.decode('So11111111111111111111111111111111111111112'); const USDC = bs58.decode('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); const userPublicKey = bs58.decode('YOUR\_WALLET\_PUBLIC\_KEY'); // Your ATA for the output mint (USDC in this case) const feeAccount = bs58.decode('YOUR\_USDC\_FEE\_ATA'); let useCompression = false; let requestId = 0; ws.on('open', async () => { useCompression = ws.protocol !== 'v1.api.titan.ag'; const id = requestId++; const encoded = encoder.encode({ id, data: { NewSwapQuoteStream: { swap: { inputMint: SOL, outputMint: USDC, amount: 1\_000\_000\_000n, // 1 SOL slippageBps: 50, }, transaction: { userPublicKey, feeAccount, feeBps: 100, // 1% fee }, }, }, }); ws.send(useCompression ? await zstdCompress(encoded) : encoded); }); \`\`\` {% endtab %} {% tab title="Titan Gateway" %} \`\`\`typescript import { decode } from '@msgpack/msgpack'; const SOL = 'So11111111111111111111111111111111111111112'; const USDC = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'; const params = new URLSearchParams({ inputMint: SOL, outputMint: USDC, amount: '1000000000', userPublicKey: 'YOUR\_WALLET\_PUBLIC\_KEY', slippageBps: '50', feeAccount: 'YOUR\_USDC\_FEE\_ATA', // ATA for the output mint feeBps: '100', // 1% fee }); const res = await fetch( \`${process.env.TITAN\_ENDPOINT}/api/v1/quote/swap?${params}\`, { headers: { 'Authorization': \`Bearer ${process.env.TITAN\_API\_KEY}\`, 'Accept': 'application/vnd.msgpack', }, } ); const buffer = await res.arrayBuffer(); const quotes = decode(new Uint8Array(buffer)) as any; \`\`\` {% endtab %} {% endtabs %} ## Collect fees on input token Set \`feeFromInputMint: true\` and make sure your fee account is an ATA for the \*\*input\*\* mint instead of the output mint. {% tabs %} {% tab title="Titan Direct" %} \`\`\`typescript import WebSocket from 'ws'; import { Encoder, Decoder } from '@msgpack/msgpack'; import bs58 from 'bs58'; import { zstdCompress, zstdDecompress } from 'http-encoding'; const encoder = new Encoder({ useBigInt64: true }); const decoder = new Decoder({ useBigInt64: true }); const url = \`${process.env.TITAN\_ENDPOINT}?auth=${process.env.TITAN\_API\_KEY}\`; const ws = new WebSocket(url, \[\ 'v1.api.titan.ag+zstd',\ 'v1.api.titan.ag',\ \]); const SOL = bs58.decode('So11111111111111111111111111111111111111112'); const USDC = bs58.decode('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); const userPublicKey = bs58.decode('YOUR\_WALLET\_PUBLIC\_KEY'); // Fee from input — ATA must be for SOL (wrapped SOL), not USDC const feeAccount = bs58.decode('YOUR\_SOL\_FEE\_ATA'); let useCompression = false; let requestId = 0; ws.on('open', async () => { useCompression = ws.protocol !== 'v1.api.titan.ag'; const id = requestId++; const encoded = encoder.encode({ id, data: { NewSwapQuoteStream: { swap: { inputMint: SOL, outputMint: USDC, amount: 1\_000\_000\_000n, slippageBps: 50, }, transaction: { userPublicKey, feeAccount, feeBps: 50, // 0.5% fee feeFromInputMint: true, }, }, }, }); ws.send(useCompression ? await zstdCompress(encoded) : encoded); }); \`\`\` {% endtab %} {% tab title="Titan Gateway" %} \`\`\`typescript import { decode } from '@msgpack/msgpack'; const SOL = 'So11111111111111111111111111111111111111112'; const USDC = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'; const params = new URLSearchParams({ inputMint: SOL, outputMint: USDC, amount: '1000000000', userPublicKey: 'YOUR\_WALLET\_PUBLIC\_KEY', slippageBps: '50', feeAccount: 'YOUR\_SOL\_FEE\_ATA', feeBps: '50', feeFromInputMint: 'true', }); const res = await fetch( \`${process.env.TITAN\_ENDPOINT}/api/v1/quote/swap?${params}\`, { headers: { 'Authorization': \`Bearer ${process.env.TITAN\_API\_KEY}\`, 'Accept': 'application/vnd.msgpack', }, } ); const buffer = await res.arrayBuffer(); const quotes = decode(new Uint8Array(buffer)) as any; \`\`\` {% endtab %} {% endtabs %} ## Read the fee from quote response Every \[\`SwapRoute\`\](/titan/developer-doc/swap-api/reference/types.md) that includes a fee has a \`platformFee\` object. Check it before your user signs — this is what you should display in your UI. \`\`\`typescript const best = Object.values(quotes.quotes as Record) .reduce((a: any, b: any) => BigInt(b.outAmount) > BigInt(a.outAmount) ? b : a); if (best.platformFee) { const feeAmount = BigInt(best.platformFee.amount); const fee\_bps = best.platformFee.fee\_bps; console.log(\`Platform fee: ${feeAmount} tokens (${fee\_bps} bps)\`); // Example: "Platform fee: 1428570 tokens (100 bps)" } // Show the fee to your user before they sign console.log(\`Output after fee: ${best.outAmount}\`); \`\`\` The \[\`PlatformFee\`\](/titan/developer-doc/swap-api/reference/types.md) type: \* \*\*\`amount\`\*\* (\`u64\`) — Absolute fee amount in the token's smallest unit. \* \*\*\`fee\_bps\`\*\* (\`u8\`) — Fee rate in basis points. ## Important notes {% hint style="warning" %} Only validated users can specify a \`feeAccount\`. Contact the Titan team to get your account approved for fee collection. {% endhint %} The fee is taken \*\*from\*\* the swap amount, not added on top. If a user swaps 1 SOL and the fee is 1%, the user receives the output for 0.99 SOL worth of input (when \`feeFromInputMint\` is true) or gets 1% less output (when fees are taken from the output side, the default). ## Related pages \* \[Stream & Execute a Swap\](/titan/developer-doc/swap-api/guides/stream-and-execute.md) — full guide with transaction building, signing, and error handling \* \[Configure Routing\](/titan/developer-doc/swap-api/guides/configure-routing.md) — filter venues and providers \* \[Types Reference\](/titan/developer-doc/swap-api/reference/types.md) — \`SwapRoute\`, \`PlatformFee\`, \`TransactionParams\`, and all type definitions \* \[Wire Protocol\](/titan/developer-doc/swap-api/reference/wire-protocol.md) — MessagePack encoding and compression --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/error-codes.md). # Error Codes When a request fails, the server returns a \`ResponseError\` with a numeric \`code\` and a human-readable \`message\`. On \*\*Titan Direct\*\*, errors arrive as an \`Error\` variant of \`ServerMessage\`. On \*\*Titan Gateway\*\*, errors are returned as HTTP status codes with a MessagePack body. ## Error response format {% tabs %} {% tab title="Titan Direct" %} \`\`\`typescript { Error: { requestId: number; // Matches the ID of the original request code: number; // Numeric error code for programmatic handling message: string; // Human-readable description for logging/debugging } } \`\`\` {% hint style="info" %} The \`message\` field contains a specific, actionable description of the error. \*\*Use the \`code\` for programmatic handling\*\* and the \`message\` for logging and debugging. {% endhint %} {% endtab %} {% tab title="Titan Gateway" %} | Status | Description | | ------------- | -------------------------------------------------------------------------------- | | \`400\` | Invalid parameters — malformed pubkey, missing required field, or invalid value. | | \`401\` | Missing or invalid authentication token. | | \`404\` | No routes found for this swap pair. | | {% endtab %} | | | {% endtabs %} | | ## Stream errors Streams can end with an error via the \`StreamEnd\` message: \`\`\`typescript { StreamEnd: { id: number; // The stream ID that has ended errorCode?: number; // Present only if the stream ended due to an error errorMessage?: string; // Human-readable reason for the error, if any } } \`\`\` {% hint style="warning" %} A \`StreamEnd\` \*\*without\*\* \`errorCode\` indicates a clean shutdown (e.g. after \`StopStream\`). A \`StreamEnd\` \*\*with\*\* \`errorCode\` means something went wrong and you should inspect the message. {% endhint %} \*\*\* ## WebSocket close codes The server may close the WebSocket connection with a specific close code: \* \*\*\`3002\`\*\* — \*\*Protocol error.\*\* The client sent an invalid or unsupported protocol string during negotiation, or violated the wire protocol after connecting. Reconnect with a valid \`Sec-WebSocket-Protocol\` header. \* \*\*\`1000\`\*\* — Normal closure. The server shut down gracefully. \* \*\*\`1001\`\*\* — Going away. The server is restarting or shutting down for maintenance. \*\*\* ## SDK error classes \*\*If you're using the\*\* \[\*\*\`@titanexchange/sdk-ts\`\*\*\](https://www.npmjs.com/package/@titanexchange/sdk-ts) \*\*TypeScript SDK\*\*, errors are thrown as typed classes you can catch and inspect: ### Connection errors \* \*\*\`ConnectionClosed\`\*\* — The WebSocket was closed unexpectedly. Properties: \`code\` (close code), \`reason\` (close reason string), \`wasClean\` (whether the close was clean). \* \*\*\`ConnectionError\`\*\* — Failed to establish or maintain the WebSocket connection. Property: \`cause\` (underlying error). \* \*\*\`InvalidProtocolError\`\*\* — The server selected an unsupported protocol string during negotiation. Property: the invalid protocol string. ### RPC errors \* \*\*\`ErrorResponse\`\*\* — The server returned an error for a specific request. Properties: \`response.code\` (numeric error code), \`response.message\` (human-readable description), \`response.requestId\`. \* \*\*\`StreamError\`\*\* — A stream ended with an error. Properties: \`streamId\`, \`errorCode\`, \`errorMessage\`. \* \*\*\`ProtocolError\`\*\* — A wire-level protocol violation. Properties: \`reason\`, \`data\`. ### Codec errors \* \*\*\`DecodeError\`\*\* — Failed to decode a MessagePack message. Properties: \`reason\`, \`value\`. ### Recommended pattern \`\`\`typescript import { ErrorResponse, StreamError, ConnectionClosed } from '@titanexchange/sdk-ts'; try { // ... SDK operations } catch (err) { if (err instanceof ErrorResponse) { // Server rejected the request — check code for programmatic handling console.error(\`RPC error ${err.response.code}: ${err.response.message}\`); } else if (err instanceof StreamError) { // Stream ended abnormally — re-open it console.error(\`Stream ${err.streamId} error: ${err.errorMessage}\`); } else if (err instanceof ConnectionClosed) { // WebSocket dropped — reconnect with backoff console.warn(\`Connection closed: code=${err.code}, clean=${err.wasClean}\`); } } \`\`\` \*\*\* ## Handling errors \* \*\*Authentication errors\*\* — Verify your token is valid, not expired, and includes the required JWT claims (\`iss\`, \`sub\`, \`aud\`, \`exp\`, \`iat\`). See \[Connection & Negotiation\](/titan/developer-doc/swap-api/reference/direct/connection.md). \* \*\*Invalid parameters\*\* — Check that pubkeys are valid base58, amounts are positive integers, and all required fields are present. \* \*\*No routes found\*\* — The swap pair may have insufficient liquidity, or routing constraints (\`dexes\`, \`excludeDexes\`, \`onlyDirectRoutes\`) may be too restrictive. \*\*Try relaxing your filters\*\* before assuming the pair is unsupported. \* \*\*Stream errors\*\* — When a stream ends unexpectedly, re-open it. \*\*Stream IDs from a previous connection are not valid after reconnect.\*\* For reconnection patterns, see \[Error Handling & Reconnect\](/titan/developer-doc/swap-api/guides/error-handling.md). \*\*\* ## Related pages \* \[Error Handling & Reconnect\](/titan/developer-doc/swap-api/guides/error-handling.md) — retry strategies, backoff logic, and reconnect patterns \* \[Wire Protocol\](/titan/developer-doc/swap-api/reference/wire-protocol.md) — message envelope format and framing details \* \[Types Reference\](/titan/developer-doc/swap-api/reference/types.md) — Types Reference — index of all type definitions --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/error-handling.md). # Error Handling & Reconnect Titan Direct is a persistent WebSocket connection. Connections drop, tokens expire, and streams end unexpectedly. This guide covers every failure mode and how to build a reconnect loop that keeps your integration running. ## Server messages The server sends one of four message types — \`Response\`, \`Error\`, \`StreamData\`, or \`StreamEnd\`. See \[Types Reference\](/titan/developer-doc/swap-api/reference/types.md) for the full type definitions. ## Server errors When a request fails, the server sends an \[\`Error\`\](/titan/developer-doc/swap-api/reference/types.md) instead of a \`Response\`. Check for the \`Error\` key in every message handler: \`\`\`typescript ws.on('message', async (raw: Buffer) => { const msg = await decodeMessage(raw); if ('Error' in msg) { const { code, message, requestId } = msg.Error; console.error(\`Request ${requestId} failed — code ${code}: ${message}\`); return; } }); \`\`\` ## Stream errors A stream can end abnormally. \[\`StreamEnd\`\](/titan/developer-doc/swap-api/reference/types.md) carries an optional \`errorCode\` and \`errorMessage\` — if present, the stream did not terminate cleanly: \`\`\`typescript ws.on('message', async (raw: Buffer) => { const msg = await decodeMessage(raw); if ('StreamEnd' in msg) { const { id, errorCode, errorMessage } = msg.StreamEnd; if (errorCode !== undefined) { console.error(\`Stream ${id} ended with error ${errorCode}: ${errorMessage}\`); // Restart the stream or reconnect } else { console.log(\`Stream ${id} ended cleanly\`); } } }); \`\`\` ## Connection drops When the WebSocket closes, all active streams are dead. Listen for the \`close\` event and trigger your reconnect logic: \`\`\`typescript ws.on('close', (code: number, reason: Buffer) => { console.warn(\`Connection closed — code: ${code}, reason: ${reason.toString()}\`); if (code !== 1000) { // Abnormal close — reconnect scheduleReconnect(); } }); ws.on('error', (err: Error) => { console.error('WebSocket error:', err.message); // 'close' will fire after this }); \`\`\` ## Token expiry The server refuses connections where the JWT \`exp\` claim is in the past. If your connection is rejected immediately, check that your token is still valid before reconnecting: \`\`\`typescript function isTokenExpired(token: string): boolean { const \[, payload\] = token.split('.'); const claims = JSON.parse(Buffer.from(payload, 'base64').toString()); return Date.now() / 1000 > claims.exp; } \`\`\` Refresh your token before calling \`connect()\` if it's close to expiry. ## Reconnect with exponential backoff The SDK has no built-in reconnect — it's your responsibility. Here's a complete reconnect loop: \`\`\`typescript import WebSocket from 'ws'; import { Encoder, Decoder } from '@msgpack/msgpack'; import bs58 from 'bs58'; import { zstdCompress, zstdDecompress } from 'http-encoding'; const encoder = new Encoder({ useBigInt64: true }); const decoder = new Decoder({ useBigInt64: true }); const BASE\_DELAY\_MS = 1\_000; const MAX\_DELAY\_MS = 30\_000; let useCompression = false; async function sendRequest(ws: WebSocket, id: number, data: Record) { const encoded = encoder.encode({ id, data }); ws.send(useCompression ? await zstdCompress(encoded) : encoded); } async function decodeMessage(raw: Buffer): Promise { const data = useCompression ? await zstdDecompress(raw) : raw; return decoder.decode(data); } async function connect(): Promise { const url = \`${process.env.TITAN\_ENDPOINT}?auth=${process.env.TITAN\_API\_KEY}\`; return new Promise((resolve, reject) => { const ws = new WebSocket(url, \[\ 'v1.api.titan.ag+zstd',\ 'v1.api.titan.ag',\ \]); ws.once('open', () => { useCompression = ws.protocol !== 'v1.api.titan.ag'; resolve(ws); }); ws.once('error', reject); }); } async function runWithReconnect() { let attempt = 0; while (true) { try { const ws = await connect(); console.log('Connected'); attempt = 0; // reset backoff on success // Set up your message handler and streams here await setupStreams(ws); // Wait for connection to close await new Promise((resolve) => ws.once('close', resolve)); } catch (err) { attempt++; const delay = Math.min(BASE\_DELAY\_MS \* 2 \*\* (attempt - 1), MAX\_DELAY\_MS); console.warn(\`Reconnect attempt ${attempt} in ${delay}ms...\`); await new Promise((resolve) => setTimeout(resolve, delay)); } } } async function setupStreams(ws: WebSocket) { let requestId = 0; // Call GetInfo first to confirm connection sendRequest(ws, requestId++, { GetInfo: {} }); ws.on('message', async (raw: Buffer) => { const msg = await decodeMessage(raw); if ('Response' in msg && 'GetInfo' in msg.Response.data) { // Connection confirmed — open your streams openQuoteStream(ws, requestId++); } if ('Error' in msg) { console.error(\`Error ${msg.Error.code}: ${msg.Error.message}\`); } if ('StreamEnd' in msg && msg.StreamEnd.errorCode !== undefined) { console.error(\`Stream error: ${msg.StreamEnd.errorMessage}\`); ws.close(); // trigger reconnect } }); } async function openQuoteStream(ws: WebSocket, id: number) { const SOL = bs58.decode('So11111111111111111111111111111111111111112'); const USDC = bs58.decode('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); sendRequest(ws, id, { NewSwapQuoteStream: { swap: { inputMint: SOL, outputMint: USDC, amount: 1\_000\_000\_000n, slippageBps: 50, }, transaction: { userPublicKey: bs58.decode('YOUR\_WALLET\_PUBLIC\_KEY'), }, }, }); } runWithReconnect(); \`\`\` {% hint style="warning" %} Stream IDs do not survive reconnects. After reconnecting, you must open a new stream — the previous stream ID is no longer valid. {% endhint %} ## Best practices \* Call \[\`GetInfo\`\](/titan/developer-doc/swap-api/reference/direct/get-info.md) after every reconnect to confirm the server is reachable before opening streams. \* If a route has \`expiresAtMs\` or \`expiresAfterSlot\` set, check these before building a transaction — a quote valid when received can go stale by the time it lands on-chain. \* Keep your reconnect loop separate from your quote processing logic so a stream error doesn't silently kill the reconnect handler. \*\*\* ## Related pages \* \[Stream & Execute a Swap\](/titan/developer-doc/swap-api/guides/stream-and-execute.md) — full guide with transaction building and error handling \* \[Error Codes\](/titan/developer-doc/swap-api/reference/error-codes.md) — numeric error code reference \* \[Connection & Negotiation\](/titan/developer-doc/swap-api/reference/direct/connection.md) — protocol negotiation details \* \[Types Reference\](/titan/developer-doc/swap-api/reference/types.md) — \`ServerMessage\`, \`ResponseError\`, \`StreamEnd\`, and all type definitions --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/platform-fees.md). # Platform Fees Titan DCA can take a platform fee on each cycle's swap output and send it to a wallet you control. Fees run through Titan's native swap fee mechanism, so they're collected at execution time, not billed separately. ## How it's configured The defaults and ceilings live on your tenant, set during onboarding: | Setting | Where it lives | Notes | | --------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------- | | Fee collection wallet | Tenant config (set at onboarding) | The Solana address that \*\*receives\*\* your fees. Provide one to collect fees; omit it to run fee-free. | | \`platformFeeBps\` | Tenant config | Your default fee in basis points (\`10\` = 0.1%). | | \`maxFeeBps\` | Tenant config | Hard ceiling on per-order overrides. | | \`platformFee.bps\` | Per-order, on \`POST /orders/intent\` | Overrides the default for one order. Must be \`0 ≤ bps ≤ maxFeeBps\`. Omit to use the tenant default. | {% hint style="warning" %} With no fee wallet configured, fees are \*\*disabled\*\* for your tenant — nothing is taken regardless of \`bps\`. The wallet is set once at onboarding and isn't changeable via the API; changing it later is an operational request to Titan. {% endhint %} The fee wallet must accept arbitrary SPL tokens, because the fee mint varies per cycle (see below) and this wallet accumulates whatever each cycle produces across multiple mints. Use a standard self-custodial Solana wallet, not a single-token deposit address. ## Which mint the fee is taken in Selection is deterministic, checked in order: 1. If the \*\*output\*\* mint is a liquid mint (USDC, USDT, or WSOL), the fee is taken in the output mint. 2. Otherwise, if the \*\*input\*\* mint is liquid, the fee is taken in the input mint. 3. Otherwise, the fee is taken in the output mint. So a \`USDC → USDT\` cycle takes the fee in USDT (output wins). A \`BONK → USDC\` cycle takes it in USDC (output is liquid). A \`BONK → WIF\` cycle falls through to rule 3 and takes it in WIF. ## Per-order override Pass \`platformFee.bps\` on \`POST /orders/intent\` to override your tenant default for a single order — including \`0\` to waive the fee on that order, subject to your contract: \`\`\`typescript await callTitanDca('/orders/intent', { method: 'POST', sub, body: { orderType: 'dca', userPubkey, platformFee: { bps: 25 }, // 0.25% on this order; must be ≤ maxFeeBps config: { /\* … \*/ }, }, }); \`\`\` A \`bps\` above your \`maxFeeBps\` returns \`400 VALIDATION\_ERROR\` with \`details.maxAllowed\` echoing the ceiling. ## Reconciling what was charged Every execution stores a fee snapshot. Read it per order via \`GET /orders/{orderId}/executions\`, or across your whole tenant for billing via \`GET /partners/me/executions\`: \`\`\`json { "platformFeeWallet": "FeEa…", "platformFeeBps": 50, "platformFeeMint": "EPjFW…", "platformFeeAmount": "25000" } \`\`\` All four \`platformFee\*\` fields populate together when a fee was charged. They're all \`null\` when no fee was taken — either the order's effective \`bps\` was \`0\`, or your tenant has no fee wallet configured. ## Related pages \* \[Create & Manage Orders\](/titan/developer-doc/dca-partner-api/guides/orders.md) — where \`platformFee.bps\` is set \* \[Order & Execution Schema\](/titan/developer-doc/dca-partner-api/reference/schema.md) — the full execution row, including the fee snapshot \* \[Endpoints → Partner reporting\](/titan/developer-doc/dca-partner-api/reference/endpoints.md#partner-reporting) — tenant-wide execution rows for billing --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/dart-swap-api/how-to-use.md). # How to Use The DART API is a standard \*\*JSON REST API\*\* — no MessagePack, no WebSocket. Just HTTP requests. Free to use without an API key, or pass a key for higher rate limits (see \[Get API Access\](/titan/developer-doc/dart-swap-api/get-api-access.md)). \*\*Base URL:\*\* \`https://api.titan.exchange/dart\` \*\*\* ## \`GET /health\` Health check. \`\`\`bash curl https://api.titan.exchange/dart/health \`\`\` \`\`\`json { "status": "ok" } \`\`\` \*\*\* ## \`GET /markets\` Returns the list of supported trading pairs. \`\`\`bash curl https://api.titan.exchange/dart/markets \`\`\` \`\`\`json { "markets": \[\ {\ "name": "SOL/USDC",\ "tokenA": "So11111111111111111111111111111111111111112",\ "tokenB": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"\ }\ \] } \`\`\` \*\*\* ## \`POST /swap\` Get a swap quote with transaction-ready instructions. Compute budget instructions are pre-configured for optimal execution. \*\*Request body (JSON):\*\* \* \*\*\`inputMint\`\*\* (string, required) — Input token mint address (base58). \* \*\*\`outputMint\`\*\* (string, required) — Output token mint address (base58). \* \*\*\`amount\`\*\* (string, required) — Raw amount in smallest unit (e.g. lamports). \* \*\*\`userPublicKey\`\*\* (string, required) — Wallet public key (base58). Must be on-curve. \* \*\*\`slippageBps\`\*\* (number, optional) — Slippage tolerance in basis points. Default: \`50\`. \* \*\*\`computeUnitPrice\`\*\* (number, optional) — Compute unit price in microLamports. Default: \`10000\`. \* \*\*\`includeDexes\`\*\* (string\\\[\], optional) — Only use these DEX venues. \* \*\*\`excludeDexes\`\*\* (string\\\[\], optional) — Exclude these DEX venues. \*\*Example:\*\* \`\`\`bash curl -X POST https://api.titan.exchange/dart/swap \\ -H "Content-Type: application/json" \\ -d '{ "inputMint": "So11111111111111111111111111111111111111112", "outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "amount": "1000000000", "userPublicKey": "YourWalletPublicKeyHere" }' \`\`\` \*\*Response:\*\* \`\`\`json { "outputAmount": "84550000", "inputAmount": "1000000000", "provider": "Titan-DART", "slippageBps": 50, "instructions": \[\ {\ "programId": "ComputeBudget111111111111111111111111111111",\ "accounts": \[\],\ "data": "AQAABAA="\ },\ {\ "programId": "ComputeBudget111111111111111111111111111111",\ "accounts": \[\ {\ "pubkey": "jitodontfronttitandart111111111111111111111",\ "isSigner": false,\ "isWritable": false\ }\ \],\ "data": "AsBcFQA="\ },\ {\ "programId": "ComputeBudget111111111111111111111111111111",\ "accounts": \[\],\ "data": "AxAnAAAAAAAA"\ },\ {\ "programId": "T1TANpTeScyeqVzzgNViGDNrkQ6qHz9KrSBS4aNXvGT",\ "accounts": \[\ {\ "pubkey": "YourWalletPublicKeyHere",\ "isSigner": true,\ "isWritable": true\ }\ \],\ "data": "..."\ }\ \], "addressLookupTables": \[\ "RyXhBMnPkYJyWEkBmYAnW7A8LCKfrEgAABB2xVZrwy3"\ \] } \`\`\` \*\*Response fields:\*\* \* \*\*\`outputAmount\`\*\* — Expected output in smallest unit. \* \*\*\`inputAmount\`\*\* — Input amount in smallest unit. \* \*\*\`provider\`\*\* — Always \`Titan-DART\`. \* \*\*\`slippageBps\`\*\* — Slippage tolerance applied. \* \*\*\`instructions\`\*\* — Swap instructions with compute budget pre-configured. \`programId\` and \`pubkey\` are base58, \`data\` is base64. \* \*\*\`addressLookupTables\`\*\* — Base58 address lookup table keys for V0 transaction compilation. \*\*Compute budget (prepended automatically):\*\* \* \*\*\`requestHeapFrame\`\*\* — 256 KB \* \*\*\`setComputeUnitLimit\`\*\* — 1,400,000 CUs \* \*\*\`setComputeUnitPrice\`\*\* — configurable (default 10,000 microLamports) \*\*Errors:\*\* \* \*\*\`400\`\*\* — Missing required fields or invalid JSON. \* \*\*\`404\`\*\* — No routes found for the given pair. \* \*\*\`429\`\*\* — Rate limit exceeded. \*\*\* ## Building a transaction The response includes all instructions ready to go — deserialize, build a V0 transaction, sign, and send: \`\`\`typescript import { Connection, PublicKey, TransactionInstruction, TransactionMessage, VersionedTransaction, } from "@solana/web3.js"; // 1. Deserialize instructions from the response const instructions = response.instructions.map( (ix) => new TransactionInstruction({ programId: new PublicKey(ix.programId), keys: ix.accounts.map((acc) => ({ pubkey: new PublicKey(acc.pubkey), isSigner: acc.isSigner, isWritable: acc.isWritable, })), data: Buffer.from(ix.data, "base64"), }) ); // 2. Fetch address lookup tables const connection = new Connection("https://api.mainnet-beta.solana.com"); const altAccounts = await Promise.all( response.addressLookupTables.map(async (key) => { const alt = await connection.getAddressLookupTable(new PublicKey(key)); return alt.value; }) ); // 3. Build V0 transaction const { blockhash } = await connection.getLatestBlockhash(); const message = new TransactionMessage({ payerKey: walletPublicKey, recentBlockhash: blockhash, instructions, }).compileToV0Message(altAccounts.filter(Boolean)); const transaction = new VersionedTransaction(message); // 4. Sign and send transaction.sign(\[wallet\]); const signature = await connection.sendTransaction(transaction); \`\`\` \*\*\* ## Related pages \* \[Overview\](/titan/developer-doc/dart-swap-api/overview.md) — What DART is, supported pairs, and fees \* \[Get API Access\](/titan/developer-doc/dart-swap-api/get-api-access.md) — Rate limits and higher-rate access --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/withdrawals.md). # Withdrawals Funds in a user's manager always come back to that user's own external wallet — the manager's policy allows nothing else. There are two ways to move them, both following the same intent → sign → confirm pattern as order creation. ## Wallet-level withdrawals These pull a chosen token out of the manager regardless of any order. Use them to power a "withdraw available balance" action. Check what's available first: \`\`\`typescript const { data } = await callTitanDca('/me/balance?hideZero=true', { sub }); // each balance row: totalBalance, lockedForFutureTxns, withdrawalPending, availableToWithdraw \`\`\` \`availableToWithdraw\` is \`max(total − locked − withdrawalPending, 0)\` — the source of truth for a "withdraw max" button. Active DCA orders lock their unspent input (\`totalAmount − amountSpent\`), so it won't be available until the order ends. Build the withdrawal tx, omitting \`amount\` to withdraw the max: \`\`\`typescript const intent = await callTitanDca('/withdraw/transaction', { method: 'POST', sub, body: { userPubkey, // fee payer & destination tokenMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', amount: '400000', // optional; omit for max }, }); // user signs intent.data.transaction, then: await callTitanDca('/withdraw/confirm', { method: 'POST', sub, body: { signedTransaction }, }); \`\`\` Titan re-runs the lock-aware check at confirm time using the \`(mint, amount)\` from the unsigned transaction it returned — so if that transaction goes stale during its 5-minute window (e.g. another withdrawal landed in the meantime), it's rejected before broadcast rather than overdrawing. | HTTP | \`error.code\` | When | | ---- | ------------------------ | ---------------------------------------------------------------------------------------------------------------- | | 400 | \`INSUFFICIENT\_AVAILABLE\` | The whole balance is locked, or the wallet is empty. | | 400 | \`FUNDS\_LOCKED\` | \`requested > available\` — some balance is locked by orders or a pending withdrawal. \`details\` has the breakdown. | | 400 | \`INSUFFICIENT\_BALANCE\` | \`requested > chainBalance\` — the wallet genuinely doesn't hold that much. | | 409 | \`ONBOARDING\_INCOMPLETE\` | Manager not fully provisioned. Re-call onboard, then retry. | ## Order-level withdrawals These return the funds tied to a single terminal order (\`completed\` / \`cancelled\` / \`failed\`) back to the external wallet. {% hint style="info" %} \*\*\`failed\` orders return automatically\*\* — Titan sends their unspent input back without any call from you (see \[Lifecycle\](/titan/developer-doc/dca-partner-api/guides/lifecycle.md#automatic-input-return)). So order-level withdrawal is mainly for \`completed\` and \`cancelled\` orders; on a failed order it usually reports \`NOTHING\_TO\_WITHDRAW\` or \`ALREADY\_WITHDRAWN\`. {% endhint %} \`\`\`typescript const intent = await callTitanDca(\`/orders/${orderId}/withdraw\`, { method: 'POST', sub }); // intent.data.withdrawalAmounts → \[{ mint, amount }, …\] — preview before the user signs await callTitanDca(\`/orders/${orderId}/withdraw/confirm\`, { method: 'POST', sub, body: { signedTransaction }, }); \`\`\` \`POST /orders/{orderId}/withdraw\` is safe to retry — each call rebuilds a fresh tx (so the user can re-prompt their wallet) and resets the inactivity timer on the automatic recovery process. \`withdrawalAmounts\` enumerates the per-mint amounts the transaction will move, one entry per non-zero mint, which is what you show the user before they sign. | HTTP | \`error.code\` | When | | ---- | --------------------- | ----------------------------------------------------------------------------------------- | | 400 | \`NOTHING\_TO\_WITHDRAW\` | No order-owned funds remain (the user likely drained them via a wallet-level withdrawal). | | 400 | \`INVALID\_STATE\` | Order isn't \`completed\` / \`cancelled\` / \`failed\`. | | 400 | \`ALREADY\_WITHDRAWN\` | A prior withdrawal already completed — including the automatic return on a failed order. | | 409 | \`EXECUTION\_IN\_FLIGHT\` | A swap attempt is still being reconciled. Self-resolving — retry shortly. | | 503 | \`RPC\_UNAVAILABLE\` | Couldn't fetch on-chain balance. Retry shortly. | ### Releasing a stuck withdrawal lock If the user dismisses the wallet prompt from \`POST /orders/{orderId}/withdraw\`, the order sits in \`withdrawalStatus: pending\`. Call \`POST /orders/{orderId}/withdraw/abandon\` to make the "Withdraw" button usable again immediately — it's idempotent. Without it, an automatic server-side process clears the lock within about 3 minutes anyway. ## Related pages \* \[Create & Manage Orders\](/titan/developer-doc/dca-partner-api/guides/orders.md) — cancel-with-withdraw builds an order-level withdrawal in one call \* \[Lifecycle & Polling\](/titan/developer-doc/dca-partner-api/guides/lifecycle.md) — \`withdrawalStatus\` transitions and automatic input return \* \[Error Codes\](/titan/developer-doc/dca-partner-api/reference/error-codes.md) — the full withdrawal error catalog --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/dart-routing.md). # DART Routing \*\*DART\*\* (Dynamic Allocation and Real Time) Routing is Titan's onchain routing engine and the world's first router that dynamically re-optimizes a trade at the exact moment of execution, not seconds before. ### The Quote to Execution Gap
Aggregators typically compute the route for a trade before the transaction is submitted to the blockchain. A route is calculated, a quote is returned to the user, and the transaction is then broadcast to the network. By the time that transaction lands in a block, anywhere from hundreds of milliseconds to several seconds may have passed. In that window, liquidity conditions across pools change. Prices shift. Other trades execute against the same routes. The route that was optimal at quote time is no longer optimal at execution time. This gap between when a route is computed and when it actually executes is an inherent limitation of all pre-execution routing systems. For small trades this gap may be small. For larger trades, the cost of this staleness compounds quickly, as the price impact and route quality have both moved in the time it took for the transaction to settle. ### How DART Works DART operates on a straightforward principle: at execution time, the combination of pools offering the best available prices for your trade wins the order. Market makers need the flexibility to adjust spreads in either direction when required. DART ensures your trade always selects the best venues, regardless of market maker's adjustments. Before the transaction is built, Titan's offchain infrastructure determines the optimal route shape: which pools to include, which venues have the deepest liquidity for your trade size, and which paths are worth considering. This is the foundation that DART builds on. At execution time, \*\*DART dynamically re-optimizes how your volume is split across a large number of pools in real time\*\*, onchain. It utilizes the full liquidity universe including:
\* Prop AMMs \* Non-prop AMMs (Orca, Meteora) \* Orderbook-based DEXes \* Mint/redeem pools More pools, smarter weight optimization, better execution. Working alongside \*\*Argos\*\*, Titan's offchain router and already the most advanced on Solana, the combination forms a hybrid routing engine. Together, they close the gap as close as possible to deliver the best execution available on Solana today. ### Best Bid Offer Guarantee Because DART resolves routing at execution time against actual onchain state, it provides a \*\*Best Bid Offer (BBO)\*\* guarantee. This means users are always filled by the market makers offering the best available quotes at the precise moment their trade executes. In traditional financial markets, BBO is the standard that regulated venues are required to achieve. It means that when you submit an order, the exchange must fill it at the best available price across all connected liquidity sources at that moment. DART brings this same guarantee onchain for the first time on Solana. The result is that users are not just getting the best route that could be found before their transaction was sent. They are getting the best route that exists at the moment their transaction lands, computed in real time from live market conditions. > \*\*DART is the de-facto standard for onchain execution on Solana.\*\* --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/getting-started/titan-dart.md). # Titan DART ## DART Settings Control how Titan routes your swaps — configure DART directly from the swap interface. ### Configuration Above the swap box, users can configure their DART settings by clicking on the DART box. For DART eligible pairs, DART defaults to \*\*Auto\*\*.
ModeBehavior
AutoTitan uses a combination of both Argos and DART to determine the optimal execution. Recommended setting.
OnlyTitan only uses DART routing.
OffTitan only will use off-chain Argos routing.
### DART Available Pairs DART routing is currently available for the following pairs, with more coming online soon. \* SOL/USDC \* SOL/USDT \* USDT/USDC \* cbBTC/USDC \* wETH/USDC \* TRUMP/USDC \* ZEC/USDC \* USD1/USDC \* HYPE/USDC \* PUMP/USDC \* PENGU/USDC \* FARTCOIN/USDC \* syrupUSD/USDC \* PYUSD/USDC \* USDG/USDC \* CASH/USDC \* AAVE/USDC \* MEGA/USDC \* SPCX/USDC \* MU/USDC ### DART Fees Titan DART charges up to maximum 1 bps per swap. Users on Titan only get routed via DART if the end execution including fees and expected slippage is better than all other routers. ### DART Status Indicator The colored dot on the DART swap page is a visual indicator that tells you the live execution status of DART at any moment. It stays static with no animation or flickering for clarity.

DART set to Only with the green status indicator confirming DART is actively executing the swap. Titan returns the best price across all quoted routes

DART is set to Off — the red indicator confirms your transaction will be executed via non-DART routes

ColorMeaning
GreenDART is live and active — it is the selected route that will actually execute your swap.
RedDART is not executing the trade — either it's not available, or another route is being used instead.
> \*\*Note:\*\* The dot does not turn green just because DART shows up in the quotes list. It only turns green when DART is the winning/selected quote that will be executed. Green truly means DART is handling your trade, not just participating in the comparison. A single glance tells you whether you're getting the DART execution experience or not, without needing to dig into route details. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/guides/onboarding.md). # Onboarding (SIWS) Every user makes exactly one signature before their first DCA order. \`POST /partner/onboard\` takes that Sign-In-with-Solana signature, provisions the user's Titan-managed manager, and records their external wallet as the funding and withdrawal address. After this, you act on the user's behalf with the \`X-Titan-User\` header alone — no further per-request signatures. The call is \*\*idempotent and resumable\*\*. Retrying with the same \`sub\` replays the same \`userId\` and \`walletAddress\`, so it's safe to call on every login if you'd rather not track who's already onboarded. ## What it provisions A single call resolves or creates the user's Titan identity (namespaced to your tenant), creates their \*\*manager\*\* with the DCA signing policy baked in, and binds their external wallet to that identity. The policy pins the manager to DCA swaps and withdrawals \*\*only\*\* to the user's own external wallet — so neither you nor Titan can move funds anywhere else. ## Build the canonical message The user signs these exact bytes with their external wallet. \`Address:\` must equal \`userPubkey\`; \`User:\` must equal the \`sub\` you send in the body. \`\`\` Titan DCA wants you to link this Solana wallet. Address: User: Issued At: Nonce: \`\`\` \`\`\`typescript function buildSiwsMessage(address: string, sub: string): string { return ( \`Titan DCA wants you to link this Solana wallet.\\n\\n\` + \`Address: ${address}\\n\` + \`User: ${sub}\\n\` + \`Issued At: ${new Date().toISOString()}\\n\` + \`Nonce: ${crypto.randomUUID()}\\n\` // trailing newline is required ); } \`\`\` {% hint style="warning" %} The signed bytes must match the canonical form exactly: LF line endings, a trailing \`\\n\` after \`Nonce:\`, and ≤ 1024 bytes. \`Issued At\` must be within ±10 minutes of server time. A mismatch returns \`400 SIWS\_INVALID\`. {% endhint %} The user's wallet signs the message bytes in your frontend; you forward the base58 signature to your backend: \`\`\`typescript const message = buildSiwsMessage(userPubkey, sub); // const { signature } = await wallet.signMessage(new TextEncoder().encode(message)); // const signatureBase58 = bs58.encode(signature); \`\`\` ## Submit it \`\`\`typescript const res = await fetch(\`${process.env.TITAN\_DCA\_BASE\_URL}/partner/onboard\`, { method: 'POST', headers: { 'X-Titan-Key': process.env.TITAN\_DCA\_API\_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ sub, userPubkey, siws: { message, signature: signatureBase58 }, }), }); const { data } = await res.json(); // { userId: "", walletAddress: "" } \`\`\` This call uses \`X-Titan-Key\` only — there's no \`X-Titan-User\` yet, because this is the call that creates the mapping. Store \`userId\`. It's the opaque Titan id you pass to the \[partner reporting\](/titan/developer-doc/dca-partner-api/reference/endpoints.md#partner-reporting) filters (\`?userId=…\`) — it is \*\*not\*\* your \`sub\`. From here on, identify the user on every call with \`X-Titan-User: \`. ## Replay and freshness Titan enforces the ±10-minute \`Issued At\` window plus the signature and ownership checks. The \`Nonce\` is opaque and not persisted, so it isn't checked for single use — a message can be re-submitted within its freshness window. That's safe because onboard is idempotent: a replay just returns the same \`userId\` / \`walletAddress\`. Generate a fresh \`Issued At\` and nonce per attempt anyway. ## Single-transaction onboarding The flow above asks the user for two signatures before their first order: the SIWS message here, then the deposit transaction. For a brand-new wallet you can collapse that to one. Pass \`onboardIfNeeded: true\` on \[\`POST /orders/intent\`\](/titan/developer-doc/dca-partner-api/guides/orders.md#create-intent-then-confirm), and if the \`X-Titan-User\` id was never onboarded, Titan provisions the manager inline and returns the deposit transaction as usual. That deposit is signed by \`userPubkey\`, so the signature doubles as the ownership proof — a wrong or unowned address can never fund the order, and until it's signed the manager is an empty wallet whose policy only permits outflows back to \`userPubkey\`. The flag is an explicit opt-in: it must be exactly \`true\`, and it only provisions \*\*new\*\* users. For an already-onboarded user it's ignored (the intent behaves as normal, including \`403 VALIDATION\_ERROR\` if \`userPubkey\` doesn't match the attested wallet). It never links an \*additional\* wallet to an existing user — that still needs the SIWS flow. {% hint style="danger" %} \*\*Build the two-step fallback before you ship the one-shot path.\*\* A wallet that's brand-new to \*you\* can still be known to \*Titan\* — the user may have used it on the Titan app or through another partner, and wallets are recognized across the whole platform, not just your tenant. When that happens, \`onboardIfNeeded: true\` returns \`409 USER\_PUBKEY\_CONFLICT\` instead of silently attaching you to that account. You must detect this \`409\` and fall back to the two-step SIWS flow, or those users can't place their first order — and it can happen the very first time you see a user. {% endhint %} Handling the \`409\`: {% stepper %} {% step %} ### The one-shot intent came back \`409\` \`POST /orders/intent\` with \`onboardIfNeeded: true\` returned \`409 USER\_PUBKEY\_CONFLICT\` — the wallet already belongs to a Titan account (yours, the Titan app's, or another partner's). {% endstep %} {% step %} ### Onboard with SIWS Collect one SIWS signature and call \`POST /partner/onboard\` as above. This proves the user owns the wallet and links your \`X-Titan-User\` id to the existing account. Idempotent and safe to retry. {% endstep %} {% step %} ### Re-issue the intent Call \`POST /orders/intent\` again \*\*without\*\* \`onboardIfNeeded\` (the user is now onboarded), then \`POST /orders/confirm\` as normal. {% endstep %} {% endstepper %} A brand-new wallet is one signature; a wallet already known to Titan is the usual two. New users — the common case — never hit the \`409\`. Still use \`POST /partner/onboard\` directly when you want to bind the wallet ahead of any deposit, or you need the \`userId\` for reporting before the first order. ## Errors | HTTP | \`error.code\` | When | | --------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | 400 | \`BAD\_REQUEST\` | Missing \`sub\` / \`userPubkey\` / \`siws.message\` / \`siws.signature\`, or invalid JSON. | | 400 | \`SIWS\_INVALID\` | Signature, canonical-form, freshness, or ownership check failed — bad signature, \`Address\` ≠ \`userPubkey\`, \`User\` ≠ \`sub\`, or \`Issued At\` outside ±10 min. | | 400 | \`PARTNER\_NOT\_CONFIGURED\` | Your tenant isn't enabled for partner onboarding. | | 401 | \`INVALID\_API\_KEY\` / \`KEY\_REVOKED\` / \`ENV\_MISMATCH\` | Standard \`X-Titan-Key\` failures. | | 409 | \`USER\_PUBKEY\_CONFLICT\` | Your \`sub\` and the attested wallet resolve to two different existing Titan identities. | | 409 | \`WALLET\_NEEDS\_USER\_CONSENT\` | The wallet exists with no DCA setup and Titan can't attach one server-side (rare edge). | | 500 / 502 | \`PROVISIONING\_FAILED\` | Provisioning error (\`502\` upstream, \`500\` unexpected). Safe to retry — the call is idempotent. | A user-scoped call against a not-fully-provisioned manager returns \`409 ONBOARDING\_INCOMPLETE\`. Re-call \`POST /partner/onboard\` (idempotent), then retry the original request. ## Related pages \* \[Quickstart\](/titan/developer-doc/dca-partner-api/quickstart.md) — onboarding in the context of the full flow \* \[Authentication\](/titan/developer-doc/dca-partner-api/reference/authentication.md) — how \`X-Titan-User\` resolves a user after onboarding \* \[Create & Manage Orders\](/titan/developer-doc/dca-partner-api/guides/orders.md) — the first thing you do once a user is onboarded --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/codex-of-knowledge/markdown.md). # Meta Aggregation DEX Aggregation is the way to go for low cost chains, but every DEX Aggregator provides different quotes. This is due to different algorithms being used, as well as different data sets.
Regardless of the technique being used or aggregator in question, Titan will combine DEX Aggregators for users, thus becoming a Meta Aggregator. A Meta Aggregator utilises quotes from each individual Aggregator and then provides the best quote to the end user. This way, the user can be confident that they will always receive the best price on offer at any time. This would be very similar to your traditional broker in equity markets. When an order is placed, the broker would contact different market makers who would supply the liquidity. These market makers would make the trades on the individual exchanges. The broker would then select the best quote and send it to the user. In this scenario, the players translated over to the crypto ecosystem would be: \* Exchange -> DEX \* Market Maker -> DEX Aggregator \* Broker -> Meta Aggregator Titan is sitting at this Meta Aggregator level to guarantee users the best price possible. #### How to Compare and Verify In order for Meta Aggregation to work, aggregator quotes have to be accurate, as well as be on the same block for them to be comparable. We have to be able to weed out inaccurate quotes as well as compensate for latency factors. Thankfully the solution for both problems is the same. Titan simulates all quotes directly on the blockchain. This shows the real amount out that a user would get if executed at that point in time. This also allows quotes to be compared as it removes the latency impacts of quotes arriving at different times. A given quote is simulated throughout its valid period to provide users the most up to date information. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order.md). # Placing Taker Orders \*\*Searchers fill orders by invoking the \`TakeOrder\` instruction (discriminator \`2\`).\*\* Orders can be partially or fully fulfilled — in both cases the taker receives their tokens immediately. \*\*\* ## Fill behavior ### Full fill When an order is fully filled, the program automatically: 1. \*\*Creates the maker's ATA\*\* for the output tokens (if needed). 2. \*\*Refunds the original rent\*\* used in the Limit Order back to the maker. 3. \*\*Reimburses any lamports\*\* back to the taker if they had to pay for any ATA creation. 4. \*\*For WSOL output\*\* — returns funds back to the maker as SOL instead of WSOL. ### Partial fill For partial fills, the taker receives their tokens immediately. The remaining input tokens stay in the program vault. \*\*The maker can withdraw filled output tokens at any time.\*\* ### Example: 100 USDC → 1 SOL with 5 BPS fee 1. User creates a limit order paying the rent and depositing 100 USDC into the vault. Price is set to \`0.01\`. 2. Adjusting for fees — when it is favourable to trade 1.0005 SOL → 100 USDC, the taker comes in and takes the full order. 3. Under the hood, taker deposits 1.0005 SOL into the vault. 100 USDC is moved from the vault to the taker's ATA, 0.0005 WSOL fee is moved to the fee receiver's wallet. 4. Contract determines the limit order is fulfilled. Since the output is SOL, the special WSOL edge case is handled: \* The contract expects a \*\*taker-owned WSOL (non-ATA) token account\*\* is passed into the call. \* The WSOL vault sends 1 SOL to this token account and \*\*closes it out to the maker\*\*, crediting their wallet balance with the 1 SOL. \* The limit order is closed and rent is sent to the taker. \* The taker sends the rent funds to the maker subtracting any rent they paid for the WSOL token account. {% hint style="info" %} \*\*For partial fills\*\*, the above example holds — just skip step 4. \*\*For non-WSOL trades\*\*, only step 4 differs: the program initializes the maker's ATA with the taker as rent payer. When the limit order closes, the taker is rebated accordingly. {% endhint %} \*\*\* ## WSOL handling When the output mint is WSOL and the order will close: \* The taker \*\*must pass a seeded (non-ATA) WSOL token account\*\* as the maker's output account. \* The program sends SOL to this account and \*\*closes it to the maker\*\*, crediting their wallet balance directly. \* The taker wraps SOL into their ATA before the take, and closes the ATA after. \*\*\* ## TakeOrder accounts \* \*\*\`0\` — \`taker\`\*\* — Taker wallet. \*\*Writable, signer.\*\* \* \*\*\`1\` — \`maker\`\*\* — Maker wallet (receives output tokens on full fill). \*\*Writable.\*\* \* \*\*\`2\` — \`inputMint\`\*\* — Input token mint. Read-only. \* \*\*\`3\` — \`outputMint\`\*\* — Output token mint. Read-only. \* \*\*\`4\` — \`limitOrder\`\*\* — Limit order PDA. \*\*Writable.\*\* \* \*\*\`5\` — \`takerInputMintTokenAccount\`\*\* — Taker's input token account (receives input tokens). \*\*Writable.\*\* \* \*\*\`6\` — \`takerOutputMintTokenAccount\`\*\* — Taker's output token account (sends output tokens + fees). \*\*Writable.\*\* \* \*\*\`7\` — \`makerOutputMintTokenAccount\`\*\* — Maker's output token account (or seeded account for WSOL). \*\*Writable.\*\* \* \*\*\`8\` — \`makerInputMintTokenAccount\`\*\* — Maker's input token account (for remaining balance on close). \*\*Writable.\*\* \* \*\*\`9\` — \`feeReceiverTokenAccount\`\*\* — Fee receiver's output token account. \*\*Writable.\*\* \* \*\*\`10\` — \`vaultManager\`\*\* — Vault manager PDA (\`\["vault\_manager"\]\`). Read-only. \* \*\*\`11\` — \`inputMintVault\`\*\* — Vault's input token account. \*\*Writable.\*\* \* \*\*\`12\` — \`outputMintVault\`\*\* — Vault's output token account. \*\*Writable.\*\* \* \*\*\`13\` — \`systemProgram\`\*\* — System program. Read-only. \* \*\*\`14\` — \`inputMintProgram\`\*\* — Token program for input mint (SPL or SPL-2022). Read-only. \* \*\*\`15\` — \`outputMintProgram\`\*\* — Token program for output mint (SPL or SPL-2022). Read-only. \* \*\*\`16\` — \`associatedTokenProgram\`\*\* — Associated Token Program. Read-only. \* \*\*\`17\` — \`instructionsSysvar\`\*\* — Instructions sysvar. Read-only. \*\*\* ## Instruction data \* \*\*Byte 0\*\* — \`discriminator\` (\`u8\`) — Always \`2\` (TakeOrder). \* \*\*Bytes 1–8\*\* — \`amount\` (\`u64\`, little-endian) — Input token amount to take. \* \*\*Bytes 9–16\*\* — \`max\_cost\_amount\` (\`u64\`, little-endian) — Maximum output tokens the taker will pay. \*\*Use \`u64::MAX\` for no limit.\*\* \* \*\*Byte 17\*\* — \`output\_mint\_token\_account\_bump\` (\`u8\`) — PDA bump for maker's output token account. \* \*\*Byte 18\*\* — \`input\_mint\_token\_account\_bump\` (\`u8\`) — PDA bump for maker's input token account. \* \*\*Byte 19\*\* — \`fee\_receiver\_output\_mint\_bump\` (\`u8\`) — PDA bump for fee receiver's output token account. \*\*\* ## Full execution code The following code shows how to create a complete set of instructions to execute a \`TakeOrder\`, including setup (ATA creation, WSOL wrapping) and cleanup (WSOL unwrapping). \`\`\`rust /// Fees to be paid to this address pub const FEE\_RECEIVER\_ADDRESS: Pubkey = pubkey!("Bq5ZzfiU3vTiJPrBJFcr98BnUy9Wc1dg9ASeycB2tX1C"); /// Derives the vault manager address and bump seed. pub fn get\_vault\_manager\_address\_and\_bump\_seed() -> (Pubkey, u8) { Pubkey::find\_program\_address(&\[b"vault\_manager"\], &TITAN\_LIMIT\_ORDER\_PROGRAM\_ID) } /// Derives the associated token address and bump seed for a given wallet and token mint. pub fn get\_associated\_token\_address\_and\_bump\_seed( wallet\_address: &Pubkey, token\_mint\_address: &Pubkey, token\_program: &Pubkey, ) -> (Pubkey, u8) { Pubkey::find\_program\_address( &\[\ &wallet\_address.to\_bytes(),\ &token\_program.to\_bytes(),\ &token\_mint\_address.to\_bytes(),\ \], &ASSOCIATED\_TOKEN\_PROGRAM\_ID, ) } /// Creates an instruction bundle to create a token account with a seed. pub fn create\_token\_account\_with\_seed\_instructions( payer: &Pubkey, authority: &Pubkey, mint: &Pubkey, seed: &str, owner: &Pubkey, ) -> Result<(Pubkey, Vec), TitanSDKError> { let token\_account = Pubkey::create\_with\_seed(payer, seed, owner) .map\_err(|\_| TitanSDKError::FailedToCreateInstruction)?; // Get minimum balance for rent exemption let token\_account\_space = spl\_token::state::Account::LEN; let lamports = 2039280u64; // Create account with seed instruction let create\_account\_ix = create\_account\_with\_seed( payer, &token\_account, payer, seed, lamports, token\_account\_space as u64, owner, ); // Initialize token account instruction let init\_account\_ix = initialize\_account(&spl\_token::id(), &token\_account, mint, authority) .map\_err(|\_| TitanSDKError::FailedToCreateInstruction)?; Ok((token\_account, vec!\[create\_account\_ix, init\_account\_ix\])) } /// Discriminator for TakeOrder instruction. mod TakeOrder { pub const DISCRIMINATOR: u8 = 2; } /// Represents a bundle of instructions, including setup instructions and the main instruction. pub struct InstructionBundle { /// A vector of setup instructions that need to be executed before the main instruction. pub setup: Vec, /// The main instruction to be executed. pub instruction: Instruction, /// Cleanup instructions to be executed after the main instruction. pub cleanup: Vec, } pub fn create\_take\_order\_instruction( // Taker of the order, signer. taker: Pubkey, // Input mint token account taker\_input\_mint\_token\_account: Pubkey, // Output mint token account w/ taker authority taker\_output\_mint\_token\_account: Pubkey, // Limit order state. limit\_order: &LimitOrder, // Input mint amount to recieve amount: u64, // Max cost taken from output mint token account // If this is breached the ixn will fail. max\_cost\_limit: Option, // Input token program \[spl / spl-2022\] input\_mint\_program: Pubkey, // Output token program \[spl / spl-2022\] output\_mint\_program: Pubkey, ) -> Result { let time\_in\_force = TimeInForce::try\_from(limit\_order.time\_in\_force)?; let max\_cost\_limit = max\_cost\_limit.unwrap\_or(u64::MAX); let remaining\_balance\_left = amount != limit\_order.get\_remaining\_amount(); let order\_will\_close = !remaining\_balance\_left || time\_in\_force == TimeInForce::ImmediateOrCancel || time\_in\_force == TimeInForce::TakeCancelsOrder; let fee\_ticks = limit\_order.fee\_ticks; let (cost, fee) = limit\_order .calculate\_costs\_and\_fee(amount, fee\_ticks)?; let output\_is\_wsol = limit\_order.output\_mint.eq(&WRAPPED\_SOL); let input\_is\_wsol = limit\_order.input\_mint.eq(&WRAPPED\_SOL); let limit\_order\_address = derive\_limit\_order\_address(limit\_order); let (vault\_manager\_address, \_) = get\_vault\_manager\_address\_and\_bump\_seed(); let maker = Pubkey::new\_from\_array(limit\_order.maker); let input\_mint = Pubkey::new\_from\_array(limit\_order.input\_mint); let output\_mint = Pubkey::new\_from\_array(limit\_order.output\_mint); let mut setup = vec!\[create\_associated\_token\_account\_idempotent(\ &taker,\ &Pubkey::new\_from\_array(FEE\_RECEIVER\_ADDRESS),\ &output\_mint,\ &output\_mint\_program,\ )\]; let mut cleanup = vec!\[\]; if output\_is\_wsol { let wsol\_ata = get\_associated\_token\_address\_with\_program\_id( &taker, &output\_mint, &output\_mint\_program, ); setup.extend\_from\_slice(&\[\ create\_associated\_token\_account\_idempotent(\ &taker,\ &taker,\ &output\_mint,\ &output\_mint\_program,\ ),\ transfer(&taker, &wsol\_ata, cost.saturating\_add(fee)),\ sync\_native(&output\_mint\_program, &wsol\_ata)?,\ \]); cleanup.push( close\_account(&output\_mint\_program, &wsol\_ata, &taker, &taker, &\[\])?, ) } let (output\_mint\_token\_account\_address, output\_mint\_token\_account\_bump) = if order\_will\_close && output\_is\_wsol { // Handle the special case here let (pk, instructions) = create\_token\_account\_with\_seed\_instructions( &taker, &taker, &output\_mint, "token\_seed", &output\_mint\_program, )?; setup.extend(instructions); (pk, 0) // Bump is not used in this case } else { // otherwise always assume its the makers output ata. get\_associated\_token\_address\_and\_bump\_seed(&maker, &output\_mint, &output\_mint\_program) }; let (input\_mint\_token\_account\_address, input\_mint\_token\_account\_bump) = if order\_will\_close && remaining\_balance\_left && input\_is\_wsol { // Handle the special case here let (pk, instructions) = create\_token\_account\_with\_seed\_instructions( &taker, &taker, &input\_mint, "token\_seed", &input\_mint\_program, )?; setup.extend(instructions); (pk, 0) // Bump is not used in this case } else { // otherwise always assume its the makers output ata. get\_associated\_token\_address\_and\_bump\_seed(&maker, &input\_mint, &input\_mint\_program) }; let (input\_mint\_vault\_address, \_) = get\_associated\_token\_address\_and\_bump\_seed( &vault\_manager\_address, &input\_mint, &input\_mint\_program, ); let (output\_mint\_vault\_address, \_) = get\_associated\_token\_address\_and\_bump\_seed( &vault\_manager\_address, &output\_mint, &output\_mint\_program, ); // Create the vault manager output token account if it doesn't exist setup.push(create\_associated\_token\_account\_idempotent( &taker, &vault\_manager\_address, &output\_mint, &output\_mint\_program, )); let fee\_receiver = Pubkey::new\_from\_array(FEE\_RECEIVER\_ADDRESS); let (fee\_receiver\_output\_mint\_token\_account, fee\_reciever\_output\_mint\_bump) = get\_associated\_token\_address\_and\_bump\_seed( &fee\_receiver, &output\_mint, &output\_mint\_program, ); let mut data = vec!\[\*instructions::TakeOrder::DISCRIMINATOR\]; data.extend\_from\_slice(&amount.to\_le\_bytes()); data.extend\_from\_slice(&max\_cost\_limit.to\_le\_bytes()); data.extend\_from\_slice(&\[\ output\_mint\_token\_account\_bump,\ input\_mint\_token\_account\_bump,\ fee\_reciever\_output\_mint\_bump,\ \]); let accounts = vec!\[\ AccountMeta::new(taker, true),\ AccountMeta::new(maker, false),\ AccountMeta::new\_readonly(input\_mint, false),\ AccountMeta::new\_readonly(output\_mint, false),\ AccountMeta::new(limit\_order\_address, false),\ AccountMeta::new(taker\_input\_mint\_token\_account, false),\ AccountMeta::new(taker\_output\_mint\_token\_account, false),\ AccountMeta::new(output\_mint\_token\_account\_address, false),\ AccountMeta::new(input\_mint\_token\_account\_address, false),\ AccountMeta::new(fee\_receiver\_output\_mint\_token\_account, false),\ AccountMeta::new\_readonly(vault\_manager\_address, false),\ AccountMeta::new(input\_mint\_vault\_address, false),\ AccountMeta::new(output\_mint\_vault\_address, false),\ AccountMeta::new\_readonly(solana\_program::system\_program::ID, false),\ AccountMeta::new\_readonly(input\_mint\_program, false),\ AccountMeta::new\_readonly(output\_mint\_program, false),\ AccountMeta::new\_readonly(ASSOCIATED\_TOKEN\_PROGRAM\_ID, false),\ AccountMeta::new\_readonly(solana\_program::sysvar::instructions::ID, false),\ \]; Ok(InstructionBundle { setup, instruction: Instruction { program\_id: TITAN\_LIMIT\_ORDER\_PROGRAM\_ID, accounts, data, }, cleanup, }) } \`\`\` \*\*\* ## Related pages \* \[Limit Orders Overview\](/titan/developer-doc/searchers-limit-orders/overview.md) — Order structure, price calculation, time-in-force, fees \* \[Limit Order Events\](/titan/developer-doc/searchers-limit-orders/events.md) — Event structure and parsing from program logs \* \[Error Codes\](/titan/developer-doc/searchers-limit-orders/error-codes.md) — Program error codes --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/configure-routing.md). # Configure Routing Titan routes through \[Argos\](/titan/developer-doc/getting-started/introduction.md) across all available venues and providers by default. You can restrict or filter routing using the parameters below — all go into the \`swap\` or \`transaction\` object of your request. ## Routing parameters These fields are part of \[\`SwapParams\`\](/titan/developer-doc/swap-api/reference/types.md): \* \*\*\`dexes\`\*\* (\`string\[\]\`) — Only route through these venues. Venues are on-chain liquidity sources — Raydium, Phoenix, Meteora, Orca, Whirlpool, PumpFun, and others. \* \*\*\`excludeDexes\`\*\* (\`string\[\]\`) — Exclude these venues from routing. All other venues remain available. \* \*\*\`venueAllowlist\`\*\* (\`Pubkey\[\]\`) — Constrain quotes to routes that only use venues (pools) whose \*\*address\*\* is in this list. Filters by individual venue address, unlike \`dexes\`/\`excludeDexes\` which filter by venue label.- \*\*\`venueBanlist\`\*\* (\`Pubkey\[\]\`) — Exclude any route that uses a venue (pool) whose address is in this list. The banlist overrides \`venueAllowlist\` — a venue in both lists is always excluded.- \*\*\`noVoteAccounts\`\*\* (\`bool\`) — Exclude a server-configured set of market-maker venues from routing. When absent or false, those venues are included as normal.- \*\*\`providers\`\*\* (\`string\[\]\`) — Only use these quote providers. Providers are the quote sources that compete to give you the best price — \`Titan\`, \`Metis\`, \`Okx\`, and others. \* \*\*\`onlyDirectRoutes\`\*\* (\`bool\`) — Skip multi-hop routes. Useful when you want predictable gas costs or need to avoid complex route topologies. \* \*\*\`addSizeConstraint\`\*\* (\`bool\`) — If true, only quotes with transactions that fit within the size constraint are returned. \* \*\*\`sizeConstraint\`\*\* (\`u32\`) — Maximum transaction size in bytes when \`addSizeConstraint\` is set. Default is set by the server, normally slightly less than 1232 to allow room for additional instructions like compute budgets. \* \*\*\`accountsLimitTotal\`\*\* (\`u16\`) — Max total accounts per route. If not set, any number that still allows an executable transaction is allowed (currently 256).- \*\*\`accountsLimitWritable\`\*\* (\`u16\`) — Max writable accounts per route. If not set, any number that still allows an executable transaction is allowed (currently 64). {% hint style="info" %} Providers and venues are independent filters. Providers decide \*who computes\* the route; venues decide \*where liquidity is sourced\*. You can combine both. {% endhint %} ## Example: combining multiple filters {% tabs %} {% tab title="Titan Direct" %} \`\`\`typescript import WebSocket from 'ws'; import { Encoder } from '@msgpack/msgpack'; import bs58 from 'bs58'; import { zstdCompress } from 'http-encoding'; const encoder = new Encoder({ useBigInt64: true }); const url = \`${process.env.TITAN\_ENDPOINT}?auth=${process.env.TITAN\_API\_KEY}\`; const ws = new WebSocket(url, \[\ 'v1.api.titan.ag+zstd',\ 'v1.api.titan.ag',\ \]); let useCompression = false; let requestId = 0; ws.on('open', async () => { useCompression = ws.protocol !== 'v1.api.titan.ag'; const encoded = encoder.encode({ id: requestId++, data: { NewSwapQuoteStream: { swap: { inputMint: bs58.decode('So11111111111111111111111111111111111111112'), outputMint: bs58.decode('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'), amount: 1\_000\_000\_000n, slippageBps: 50, dexes: \['Raydium', 'Whirlpool', 'Phoenix'\], // Only these venues providers: \['Titan', 'Metis'\], // Only these providers onlyDirectRoutes: true, // No multi-hop addSizeConstraint: true, accountsLimitTotal: 40, }, transaction: { userPublicKey: bs58.decode('YOUR\_WALLET\_PUBLIC\_KEY'), }, }, }, }); ws.send(useCompression ? await zstdCompress(encoded) : encoded); }); \`\`\` {% endtab %} {% tab title="Titan Gateway" %} \`\`\`typescript import { decode } from '@msgpack/msgpack'; const params = new URLSearchParams({ inputMint: 'So11111111111111111111111111111111111111112', outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', amount: '1000000000', userPublicKey: 'YOUR\_WALLET\_PUBLIC\_KEY', slippageBps: '50', dexes: 'Raydium,Whirlpool,Phoenix', providers: 'Titan,Metis', onlyDirectRoutes: 'true', addSizeConstraint: 'true', accountsLimitTotal: '40', }); const res = await fetch( \`${process.env.TITAN\_ENDPOINT}/api/v1/quote/swap?${params}\`, { headers: { 'Authorization': \`Bearer ${process.env.TITAN\_API\_KEY}\`, 'Accept': 'application/vnd.msgpack', }, } ); const buffer = await res.arrayBuffer(); const quotes = decode(new Uint8Array(buffer)) as any; \`\`\` {% endtab %} {% endtabs %} ## List available venues and providers Query the server at runtime to discover which venues and providers are currently active. {% tabs %} {% tab title="Titan Direct" %} \`\`\`typescript // List all venues with their program IDs sendRequest({ GetVenues: { includeProgramIds: true } }); // List all active providers sendRequest({ ListProviders: {} }); \`\`\` {% endtab %} {% tab title="Titan Gateway" %} \`\`\`typescript import { decode } from '@msgpack/msgpack'; const venuesRes = await fetch( \`${process.env.TITAN\_ENDPOINT}/api/v1/venues?includeProgramIds=true\`, { headers: { 'Authorization': \`Bearer ${process.env.TITAN\_API\_KEY}\`, 'Accept': 'application/vnd.msgpack', }, } ); const venues = decode(new Uint8Array(await venuesRes.arrayBuffer())) as any; console.log('Available venues:', venues.labels); const providersRes = await fetch( \`${process.env.TITAN\_ENDPOINT}/api/v1/providers\`, { headers: { 'Authorization': \`Bearer ${process.env.TITAN\_API\_KEY}\`, 'Accept': 'application/vnd.msgpack', }, } ); const providers = decode(new Uint8Array(await providersRes.arrayBuffer())) as any\[\]; console.log('Active providers:', providers.map((p: any) => p.id)); \`\`\` {% endtab %} {% endtabs %} ## Related pages \* \[Stream & Execute a Swap\](/titan/developer-doc/swap-api/guides/stream-and-execute.md) — full guide with transaction building and error handling \* \[Fee Collection\](/titan/developer-doc/swap-api/guides/fee-collection.md) — add platform fees to swap transactions \* \[Types Reference\](/titan/developer-doc/swap-api/reference/types.md) — \`SwapParams\`, \`TransactionParams\`, and all type definitions \* \[GetVenues / ListProviders Reference\](/titan/developer-doc/swap-api/reference/direct/venues-providers.md) — complete response schemas --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/quickstart.md). # Quickstart This walks through the full path: onboard a user once, then create a DCA order with the two-step intent/confirm flow. Everything runs from your backend with your \`X-Titan-Key\`. {% hint style="info" %} You'll need your partner \*\*API key\*\* and the \*\*base URL\*\* for your environment — both issued at onboarding. Keep the key in your backend secret store; never expose it to a browser. {% endhint %} {% stepper %} {% step %} ### Set your credentials \`\`\`bash export TITAN\_DCA\_API\_KEY="" export TITAN\_DCA\_BASE\_URL="https://api.chronos.titan.exchange/api/v1" \`\`\` A small helper for every user-scoped call — note \`X-Titan-User\`, not a bearer token: \`\`\`typescript async function callTitanDca(path: string, opts: { method?: string; body?: unknown; sub: string; // your stable user id == X-Titan-User }) { const res = await fetch(\`${process.env.TITAN\_DCA\_BASE\_URL}${path}\`, { method: opts.method ?? 'GET', headers: { 'X-Titan-Key': process.env.TITAN\_DCA\_API\_KEY!, 'X-Titan-User': opts.sub, 'Content-Type': 'application/json', }, body: opts.body ? JSON.stringify(opts.body) : undefined, }); return res.json(); } \`\`\` {% endstep %} {% step %} ### Onboard the user (once) Have the user sign a canonical SIWS message with their external wallet, then post it. This provisions their Titan-managed manager and records their external wallet as the funding/withdrawal address. It's idempotent — re-calling with the same inputs replays the same result. \`\`\`typescript const message = \`Titan DCA wants you to link this Solana wallet.\\n\\n\` + \`Address: ${userPubkey}\\n\` + \`User: ${sub}\\n\` + \`Issued At: ${new Date().toISOString()}\\n\` + \`Nonce: ${crypto.randomUUID()}\\n\`; // The user's own wallet signs \`message\` in your frontend; you send the signature here. const res = await fetch(\`${process.env.TITAN\_DCA\_BASE\_URL}/partner/onboard\`, { method: 'POST', headers: { 'X-Titan-Key': process.env.TITAN\_DCA\_API\_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ sub, userPubkey, siws: { message, signature: signatureBase58 }, }), }); const { data } = await res.json(); // Store data.userId — you'll pass it to /partners/me/\* reporting filters. \`\`\` \`User:\` must equal the \`sub\` you'll send on every later call; \`Address:\` must equal the wallet pubkey. See \[Onboarding\](/titan/developer-doc/dca-partner-api/guides/onboarding.md) for the exact message format and freshness rules. {% endstep %} {% step %} ### Create a DCA order — get an unsigned deposit tx The order's input is funded by the user's external wallet, so creation is two steps. First, \`intent\` returns an unsigned deposit transaction: \`\`\`typescript const intent = await callTitanDca('/orders/intent', { method: 'POST', sub, body: { orderType: 'dca', userPubkey, config: { inputMint: 'So11111111111111111111111111111111111111112', // SOL outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC totalAmount: '1000000000', // 1 SOL total, in lamports amountPerCycle: '100000000', // 0.1 SOL per cycle cycleFrequencySeconds: 86400, // daily }, }, }); const { pendingOrderId, transaction } = intent.data; // transaction is base64, unsigned \`\`\` The user signs \`transaction\` with their external wallet within the 5-minute window. Network fees on the recurring cycle executions are sponsored by Titan — the manager never needs SOL to keep running. {% endstep %} {% step %} ### Confirm with the signed transaction \`\`\`typescript const confirmed = await callTitanDca('/orders/confirm', { method: 'POST', sub, body: { pendingOrderId, signedTransaction, // base64, signed by the user's external wallet }, }); const { order, txSignature } = confirmed.data; \`\`\` Titan co-signs, submits to Solana, and activates the order. From here, Titan runs each cycle at the configured cadence — no further action from you. {% endstep %} {% endstepper %} ## What success looks like After \`confirm\` returns, the order is \`active\`: \`\`\`json { "success": true, "data": { "order": { "id": "9b3f1ad0-…", "status": "active", "orderType": "dca", "...": "…" }, "txSignature": "5Uq…", "pendingOrderId": "9b3f1ad0-…" } } \`\`\` ## Manage it \`\`\`typescript await callTitanDca('/me/orders/active', { sub }); // list running orders await callTitanDca(\`/dca/${orderId}\`, { sub }); // one order with progress await callTitanDca(\`/orders/${orderId}/pause\`, { method: 'POST', sub }); await callTitanDca(\`/orders/${orderId}/resume\`, { method: 'POST', sub }); \`\`\` For back-office reconciliation across your whole tenant, use the partner reporting routes with \`X-Titan-Key\` only — see \[Endpoints → Partner reporting\](/titan/developer-doc/dca-partner-api/reference/endpoints.md#partner-reporting). ## Related pages \* \[Onboarding (SIWS)\](/titan/developer-doc/dca-partner-api/guides/onboarding.md) — the canonical message, freshness, and error cases \* \[Create & Manage Orders\](/titan/developer-doc/dca-partner-api/guides/orders.md) — the full two-step flow, modify, pause, cancel \* \[Authentication\](/titan/developer-doc/dca-partner-api/reference/authentication.md) — headers, request tiers, auth errors --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/authentication.md). # Authentication The DCA Partner API uses two headers. There's no OAuth, no token exchange, and no partner-signed JWT — your backend holds one API key and identifies users by the same id you already use for them. The only signature anywhere in the flow is the user's one-time SIWS at \[onboarding\](/titan/developer-doc/dca-partner-api/guides/onboarding.md). | Header | Required on | Purpose | | -------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------- | | \`X-Titan-Key\` | \*\*every\*\* request | Authenticates your integration (your tenant). Keep it server-side; never ship it to a browser. | | \`X-Titan-User\` | every \*\*user-scoped\*\* request | Your stable id for the end-user — the same value you onboarded as \`sub\`. Titan maps it to that user's manager. | Every header originates on your backend. Nothing goes from the end-user's browser directly to Titan. | Header | Value | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | \`X-Titan-Key\` | Your partner API key. Never expose to browsers. | | \`X-Titan-User\` | Your stable id for the user the request acts on. Required on user-scoped routes. | | \`Content-Type\` | \`application/json\` on POST/PATCH. | | \`X-Idempotency-Key\` | Optional on selected POSTs. A stable, unique-per-operation string. See \[Idempotency\](/titan/developer-doc/dca-partner-api/reference/limits.md#idempotency). | | \`X-Request-Id\` | Optional. If set, Titan keeps it in logs and echoes it back as \`x-request-id\`. Otherwise one is generated. | ## Request tiers | Tier | Headers | Endpoints | | ---------------- | ------------------------------ | ----------------------------------------------------------------------------------- | | \*\*Public\*\* | none | \`GET /health\` | | \*\*Partner-only\*\* | \`X-Titan-Key\` | \`POST /partner/onboard\`, \`GET /partners/me/\*\` (reporting) | | \*\*User-scoped\*\* | \`X-Titan-Key\` + \`X-Titan-User\` | Everything that acts on a specific user — balances, orders, withdrawals, \`GET /me\`. | {% hint style="info" %} Reporting is \*\*tenant-scoped, not user-header-scoped\*\*. The \`/partners/me/\*\` endpoints use \`X-Titan-Key\` only and return rows across your whole tenant. To narrow them to one user, pass \`?userId=\` — that \`userId\` is the opaque Titan id, \*\*not\*\* your \`sub\` / \`X-Titan-User\`. This is the main reason to store the \`userId\` onboarding returns. {% endhint %} ## User consent A user's external wallet is bound to their manager by a SIWS signature collected once at \`POST /partner/onboard\`. After that, you act on their behalf within the on-chain policy by sending \`X-Titan-User\` — no further per-request user signature. The policy pins the manager to DCA swaps and withdrawals only to that user's own external wallet, so neither you nor Titan can move funds anywhere else. ## Auth errors These apply to every authenticated endpoint: | HTTP | \`error.code\` | When | | ---- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- | | 401 | \`INVALID\_API\_KEY\` | Missing or unknown \`X-Titan-Key\`. | | 401 | \`KEY\_REVOKED\` | Your key was revoked. | | 401 | \`ENV\_MISMATCH\` | Key issued for a different environment. | | 401 | \`UNAUTHORIZED\` | On a user-scoped route: \`X-Titan-User\` is missing, or the supplied id was never onboarded. Call \`POST /partner/onboard\` for that user first. | | 403 | \`PRODUCT\_DISABLED\` | The DCA product is disabled on your key. | | 403 | \`PRODUCT\_EXPIRED\` | The DCA grant has expired. | | 403 | \`TENANT\_SUSPENDED\` | Your tenant is suspended (recoverable; contact Titan). | | 403 | \`TENANT\_DELETED\` | Your tenant is deleted. | | 403 | \`TENANT\_NOT\_PROVISIONED\` | Key valid, but no tenant row yet — onboarding step missing on Titan's side. | ## Verify your plumbing \`GET /me\` resolves the identity for the current request and is the quickest smoke test that \`X-Titan-Key\` + \`X-Titan-User\` map to the user you expect: \`\`\`json { "success": true, "data": { "userId": "", "walletAddress": "GZk2v…", "sessionId": "" } } \`\`\` \`sessionId\` is empty for partner integrations — it's a browser-session field with no server-side equivalent. Rely on \`userId\` / \`walletAddress\`, and treat both as opaque strings (the field names are stable; their format may change). ## Related pages \* \[Onboarding (SIWS)\](/titan/developer-doc/dca-partner-api/guides/onboarding.md) — how the \`X-Titan-User\` → manager mapping gets created \* \[Endpoints\](/titan/developer-doc/dca-partner-api/reference/endpoints.md) — the tier each route belongs to \* \[Error Codes\](/titan/developer-doc/dca-partner-api/reference/error-codes.md) — the complete error catalog --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits.md). # Limits & Idempotency ## Limits | Limit | Value | | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | List endpoints | At most \*\*500 rows\*\*, unless stated otherwise. Use \`createdAtGte\` / \`createdAtLte\` on \`/partners/me/\*\` for older data. | | \`GET /orders/pending/history\` | \*\*50 rows\*\* — the one exception to the 500 cap. | | SIWS message | Max \*\*1024 bytes\*\*; signed bytes must match the canonical form exactly (LF endings, trailing \`\\n\` after \`Nonce:\`); \`Issued At\` within \*\*±10 minutes\*\*. | | Cycle frequency | Minimum \*\*60 seconds\*\* (\`cycleFrequencySeconds\`). | | Per-cycle value | \`amountPerCycle\` must be worth at least \*\*$10 USD\*\* by default (configurable per tenant). Enforced at create and whenever \`amountPerCycle\` changes. USDC/USDT count as $1.00; other mints are priced by the oracle at request time. | | Pending order TTL | \*\*5 minutes\*\* between \`intent\` and \`confirm\`. | | Modification lock | \*\*30 seconds\*\* between a modify-intent returning \`requiresTransaction: true\` and \`modify/confirm\`. | | Idempotency replay window | \*\*24 hours\*\*. Only \`2xx\` responses are cached. | | Order-level withdrawal auto-recovery | Clears a stuck \`withdrawalStatus: pending\` roughly \*\*3 minutes\*\* after it gets stuck. | | API key environment | A key is bound to exactly one environment (\`staging\` or \`production\`). | | Rate limits | Configured per partner during onboarding. | ## Idempotency Every authenticated \`POST\` under the user-scoped tier accepts an \`X-Idempotency-Key\` header — \`/orders/intent\`, \`/orders/confirm\`, \`/dca/{id}/modify/confirm\`, \`/orders/{id}/cancel\`, \`/orders/{id}/withdraw\`, \`/orders/{id}/withdraw/confirm\`, \`/orders/{id}/withdraw/abandon\`, \`/orders/{id}/pause\`, \`/orders/{id}/resume\`, \`/orders/{id}/retry\`, \`/withdraw/transaction\`, and \`/withdraw/confirm\`. The header is optional — omit it on first-fire calls; include it on calls you intend to retry. \`POST /partner/onboard\` doesn't need a key — it's inherently idempotent, since re-calling with the same \`sub\` replays the same \`userId\` / \`walletAddress\`. Idempotency isn't applied to GET endpoints or partner-only endpoints. \*\*The rules:\*\* \* A key is scoped per \*\*(your tenant, end-user, key string)\*\* — two different users sharing a key value don't collide. \* Same key \*\*+ same body\*\* → Titan replays the exact cached JSON response (with the original \`2xx\` status) for \*\*24 hours\*\*. \* Same key \*\*+ different body\*\* → \`422 IDEMPOTENCY\_KEY\_REUSED\`. Choose a fresh key. \* \*\*Only \`2xx\` responses are cached.\*\* If the first call returns \`4xx\` or \`5xx\`, the key isn't recorded — a retry with the same key runs the handler again. A failed \`/orders/intent\` (e.g. a bad mint) shouldn't be permanently bound to a request body. Use a deterministic, client-side key per logical user action — \`dca:create::v1\`. Don't randomize per network attempt; the whole point is that retries hash to the same key. ## Polling, not webhooks Titan DCA doesn't push state changes — there are no webhooks today. Poll the read endpoints while a user is on a DCA surface, and follow the cadence guidance in \[Lifecycle & Polling\](/titan/developer-doc/dca-partner-api/guides/lifecycle.md). ## Related pages \* \[Create & Manage Orders\](/titan/developer-doc/dca-partner-api/guides/orders.md) — where idempotency keys matter most \* \[Error Codes\](/titan/developer-doc/dca-partner-api/reference/error-codes.md) — \`IDEMPOTENCY\_KEY\_REUSED\` and the retryable codes \* \[Lifecycle & Polling\](/titan/developer-doc/dca-partner-api/guides/lifecycle.md) — polling cadence and back-off --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/resources/ai-llm-integration.md). # AI / LLM Integration \*\*Use Titan's swap infrastructure from AI agents, LLM tool-calling workflows, and autonomous trading systems.\*\* \*\*\* ## Claude Code Skill \*\*The fastest way to build Titan integrations with AI.\*\* The \`@titanexchange/titan-api-skill\` package gives Claude Code protocol-aware code generation for the Titan API. \* \*\*Package:\*\* \[@titanexchange/titan-api-skill\](https://www.npmjs.com/package/@titanexchange/titan-api-skill) \* \*\*Source:\*\* \[github.com/Titan-Pathfinder/titan-api-claude-skills\](https://github.com/Titan-Pathfinder/titan-api-claude-skills) ### What it provides \* \*\*Protocol-aware code generation\*\* — Generates TypeScript with correct MessagePack encoding, BigInt amounts, and bs58-decoded token mints matching the Titan WebSocket API spec. \* \*\*SDK and raw WebSocket support\*\* — Covers both SDK-based and direct WebSocket integration depending on developer needs. \* \*\*Parameter structure enforcement\*\* — Places fields like \`slippageBps\`, \`intervalMs\`, and \`num\_quotes\` in their correct nested objects matching the expected request schema. \* \*\*Runnable examples included\*\* — Ships with working TypeScript examples that can be executed directly. ### Installation \*\*npx (recommended):\*\* \`\`\`bash # For current project npx @titanexchange/titan-api-skill # For all projects (global) npx @titanexchange/titan-api-skill --global # Overwrite existing install npx @titanexchange/titan-api-skill --force \`\`\` Then type \`/titan-swap-api\` in Claude Code to use the skill. \*\*Manual install (curl):\*\* \`\`\`bash # Global mkdir -p ~/.claude/skills/titan-swap-api curl -o ~/.claude/skills/titan-swap-api/SKILL.md \\ https://raw.githubusercontent.com/Titan-Pathfinder/titan-api-claude-skills/main/SKILL.md # Project-level mkdir -p .claude/skills/titan-swap-api curl -o .claude/skills/titan-swap-api/SKILL.md \\ https://raw.githubusercontent.com/Titan-Pathfinder/titan-api-claude-skills/main/SKILL.md \`\`\` ### Quick example Ask Claude Code: > "Help me stream USDC to SOL quotes using Titan API" Claude will generate protocol-correct code: \`\`\`typescript import { V1Client } from "@titanexchange/sdk-ts"; import bs58 from "bs58"; const client = await V1Client.connect(\`${WS\_URL}?auth=${AUTH\_TOKEN}\`); const { stream } = await client.newSwapQuoteStream({ swap: { inputMint: bs58.decode("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), outputMint: bs58.decode("So11111111111111111111111111111111111111112"), amount: BigInt(100\_000\_000), // 100 USDC — must be BigInt! }, transaction: { userPublicKey: bs58.decode(USER\_PUBLIC\_KEY), }, }); for await (const quotes of stream) { console.log(quotes); } \`\`\` ### Runnable examples The \`/examples\` directory contains working TypeScript examples: \`\`\`bash cd examples npm install cp .env.example .env # Edit .env with your credentials npm run stream-sdk # SDK streaming npm run stream-raw # Raw WebSocket npm run proxy # Backend proxy \`\`\` ### Required credentials \* \*\*\`WS\_URL\`\*\* — Titan WebSocket endpoint. \* \*\*\`AUTH\_TOKEN\`\*\* — API authentication token. See \[Get API Access\](/titan/developer-doc/getting-started/api-access.md). \*\*\* ## LLM-friendly docs (llms.txt) \*\*Titan's documentation is automatically available in LLM-optimized formats\*\* via the \[llms.txt\](https://llmstxt.org/) standard. AI agents and LLMs can ingest the full docs without scraping HTML. \* \*\*\`/llms.txt\`\*\* — Index of all doc sections with links to individual pages in Markdown format. \* \*\*\`/llms-full.txt\`\*\* — The entire documentation as a single Markdown file — ideal for full-context ingestion. \* \*\*Every page as \`.md\`\*\* — Append \`.md\` to any doc page URL to get the raw Markdown version. {% hint style="info" %} These endpoints are \*\*generated automatically by GitBook\*\* and stay up-to-date with every publish. No configuration needed. {% endhint %} \*\*\* ## Key things to know \* \*\*Protocol\*\* — WebSocket + MessagePack (not JSON). \* \*\*Amounts\*\* — Must be \`BigInt\`, not \`number\`. \* \*\*Token mints\*\* — Must be \`Uint8Array\` via \`bs58.decode()\`. \* \*\*Parameters\*\* — \`slippageBps\` goes in \`swap\`, \`intervalMs\` goes in \`update\`. \*\*\* ## Related pages \* \[SDK Reference\](/titan/developer-doc/resources/sdk.md) — TypeScript and Rust SDK documentation \* \[Connection & Negotiation\](/titan/developer-doc/swap-api/reference/direct/connection.md) — WebSocket setup and protocol details \* \[Quickstart\](/titan/developer-doc/swap-api/quickstart.md) — End-to-end integration example --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/guides/swap-v2-vs-v3.md). # Swap V2 vs Swap V3 Swap V3 is the newer version of the Titan Exchange Router. V2 is the current default; V3 is opt-in via \`titanSwapVersion: 3\`. {% hint style="warning" %} \*\*The default is changing to V3 on July 15, 2026.\*\* Today, requests without \`titanSwapVersion\` use V2. After the switch, requests without \`titanSwapVersion\` will use V3. V2 will remain available as an explicit opt-in (\`titanSwapVersion: 2\`). See \[Migrating to V3\](#migrating-to-v3) below. {% endhint %} ## What's new in V3 \*\*Account handling moved inside the instruction.\*\* Output ATA creation and wSOL wrapping/unwrapping are handled inside the swap instruction itself, so a V3 route returns a single consolidated swap instruction where V2 returned several separate ones. This reduces the number of instructions you assemble into the transaction. \*\*Separate fee payer (\`payer\`).\*\* A distinct account can fund all SOL-denominated costs — network fees, ATA rent (wSOL wrap, output ATA), and rent refunds — instead of the user paying them. The payer must co-sign the transaction. Defaults to the user if not set. Enables sponsored / gasless-style swap flows. \*\*Positive-slippage capture (\`positiveSlippageFeeReceiver\`).\*\* When realized output beats the quoted amount, the surplus can be skimmed to a designated account, capped at 10 bps of the output. The receiver must be an existing token account of the output mint. Anything above the cap stays with the user. \*\*Keep output as wSOL (\`outputWsol\`).\*\* When the output mint is wrapped SOL (\`So11111111111111111111111111111111111111112\`), the router unwraps the result to native SOL by default. Set \`outputWsol: true\` to leave the output as the wSOL SPL token instead — useful when the next step in your flow expects a token account rather than native lamports. Boolean, defaults to \`false\`. Only has an effect when \`outputMint\` is wSOL. \`\`\`jsonc { "transaction": { "userPublicKey": "72neGwRAi6QWsFQjy3PkDuYBC5GNCRwC2aUMGcrkoJuP", "outputWsol": true } } \`\`\` ## Selecting a version V3 is selected per request via the \`titanSwapVersion\` field in \`TransactionParams\`. Leave it unset for V2 (the default); set it to \`3\` to opt into V3 and unlock \`payer\`, \`positiveSlippageFeeReceiver\`, and \`outputWsol\`. \`\`\`jsonc { "transaction": { "userPublicKey": "72neGwRAi6QWsFQjy3PkDuYBC5GNCRwC2aUMGcrkoJuP", "titanSwapVersion": 3 } } \`\`\` > \`titanSwapVersion\` is the integer \`3\`, \*\*not\*\* the string \`"V3"\`. A string is rejected with \`Failed to deserialize query string: titanSwapVersion: invalid digit found in string\`. ## Pinning to V2 If you aren't ready to migrate before the \[default switches to V3\](#swap-v2-vs-swap-v3) on July 15, 2026, set \`titanSwapVersion: 2\` explicitly. Requests that pin the version this way are unaffected by the default change and keep using V2 until it is fully retired. \`\`\`jsonc { "transaction": { "userPublicKey": "72neGwRAi6QWsFQjy3PkDuYBC5GNCRwC2aUMGcrkoJuP", "titanSwapVersion": 2 } } \`\`\` {% hint style="info" %} Pinning to V2 is a stopgap, not a long-term position. Treat the pin as a way to buy migration time — not to stay on V2 indefinitely. {% endhint %} ## Migrating to V3 To move to V3, set \`titanSwapVersion: 3\` in \`TransactionParams\`. Once V3 becomes the default on July 15, 2026, you can drop the field entirely. V3 adds three optional fields in \`TransactionParams\`. None are required to migrate — adopt them only if you need what they provide: \* \*\*\`payer\`\*\* — a separate account to fund the SOL-denominated costs of the swap. Must co-sign the transaction. Defaults to the user's public key if unset. \* \*\*\`positiveSlippageFeeReceiver\`\*\* — the account that receives any positive-slippage surplus. \* \*\*\`outputWsol\`\*\* — leave the output as wrapped SOL instead of unwrapping to native SOL, when the output mint is wSOL. Defaults to \`false\`. If you build the transaction yourself from the route's \`instructions\` and \`addressLookupTables\`, note that the V3 instruction set differs from V2 — a V3 route returns a single consolidated swap instruction where V2 returned several. Rebuild from the returned \`instructions\` rather than assuming the V2 layout. When reading quotes, set the transaction's compute-unit limit from the quote's \`computeUnitsSafe\` — the server's recommended value with a buffer — rather than a hard-coded number. ### Migration checklist 1. Set \`titanSwapVersion: 3\` on your swap requests. 2. Adopt \`payer\`, \`positiveSlippageFeeReceiver\`, or \`outputWsol\` only if your flow needs them. 3. If you set compute-unit limits manually, use the quote's \`computeUnitsSafe\`. 4. After July 15, 2026 you can drop \`titanSwapVersion\` entirely — V3 becomes the default. ## Comparison | | Swap V2 | Swap V3 | | ------------------------------ | ------------------------------------------------------------------------ | -------------------------------------------------------------------- | | Status | Current default; becomes opt-in (\`titanSwapVersion: 2\`) on July 15, 2026 | Opt-in now (\`titanSwapVersion: 3\`); becomes default on July 15, 2026 | | ATA creation + SOL wrap/unwrap | Separate instructions around the swap | Handled inside the swap instruction (one consolidated instruction) | | Separate fee payer | No | Yes (\`payer\`, must co-sign) | | Positive-slippage capture | No | Yes (≤ 10 bps, output-mint token account) | | Keep output as wSOL | No | Yes (\`outputWsol\`) | --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/types.md). # Types Reference All type definitions live inline on the page where they're used. This page is an index for quick navigation. \*\*Field names are \`camelCase\` on the wire (MessagePack) unless otherwise specified.\*\* Rust SDK types use \`snake\_case\` with serde renaming. \*\*\* ## Wire protocol & common types Defined on \[Wire Protocol\](/titan/developer-doc/swap-api/reference/wire-protocol.md): \* \*\*\`Pubkey\`\*\* — 32-byte Solana public key \* \*\*\`AccountMeta\`\*\* — compact account descriptor (\`p\`, \`s\`, \`w\`) \* \*\*\`Instruction\`\*\* — on-chain instruction (\`p\`, \`a\`, \`d\`) \* \*\*\`SwapMode\`\*\* — \`ExactIn\` or \`ExactOut\` \* \*\*\`ClientRequest\`\*\* — request envelope with \`id\` and \`data\` \* \*\*\`ServerMessage\`\*\* — \`Response\`, \`Error\`, \`StreamData\`, or \`StreamEnd\` \* \*\*\`ResponseSuccess\`\*\* / \*\*\`ResponseError\`\*\* — success and error response types \* \*\*\`StreamData\`\*\* / \*\*\`StreamEnd\`\*\* / \*\*\`StreamStart\`\*\* — stream lifecycle types \*\*\* ## Server info types Defined on \[GetInfo\](/titan/developer-doc/swap-api/reference/direct/get-info.md): \* \*\*\`ServerInfo\`\*\* — protocol version and server settings \* \*\*\`VersionInfo\`\*\* — \`major\`, \`minor\`, \`patch\` \* \*\*\`ServerSettings\`\*\* — quote update, swap, transaction, and connection settings \* \*\*\`BoundedValueWithDefault\`\*\* — \`min\`, \`max\`, \`default\` \* \*\*\`QuoteUpdateSettings\`\*\* / \*\*\`SwapSettings\`\*\* / \*\*\`TransactionSettings\`\*\* / \*\*\`ConnectionSettings\`\*\* \*\*\* ## Request types Defined on \[NewSwapQuoteStream\](/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream.md): \* \*\*\`SwapQuoteRequest\`\*\* — \`swap\` + \`transaction\` + \`update\` \* \*\*\`SwapParams\`\*\* — input/output mints, amount, slippage, routing filters \* \*\*\`TransactionParams\`\*\* — wallet key, fee config, token account options \* \*\*\`QuoteUpdateParams\`\*\* — stream interval and quote count \*\*\* ## Quote & stream data types Defined on \[NewSwapQuoteStream\](/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream.md#stream-updates): \* \*\*\`SwapQuotes\`\*\* — quote batch with provider-keyed \`quotes\` map \* \*\*\`SwapRoute\`\*\* — single route with instructions, ALTs, expiry, compute budget \* \*\*\`RoutePlanStep\`\*\* — one hop in a multi-step route \* \*\*\`PlatformFee\`\*\* — \`amount\` and \`fee\_bps\` \*\*\* ## Discovery types Defined on \[GetVenues / ListProviders\](/titan/developer-doc/swap-api/reference/direct/venues-providers.md): \* \*\*\`VenueInfo\`\*\* — venue labels and optional program IDs \* \*\*\`ProviderInfo\`\*\* — provider id, name, kind, optional icon \* \*\*\`ProviderKind\`\*\* — \`"DexAggregator"\` or \`"RFQ"\` \*\*\* ## Stop stream types Defined on \[StopStream\](/titan/developer-doc/swap-api/reference/direct/stop-stream.md): \* \*\*\`StopStreamRequest\`\*\* — stream ID to stop \* \*\*\`StopStreamResponse\`\*\* — confirmed stream ID \*\*\* ## Related pages \* \[Wire Protocol\](/titan/developer-doc/swap-api/reference/wire-protocol.md) — encoding rules, common types, and message envelope types \* \[Connection & Negotiation\](/titan/developer-doc/swap-api/reference/direct/connection.md) — WebSocket setup and authentication \* \[NewSwapQuoteStream\](/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream.md) — request, response, and stream data types \* \[GetInfo\](/titan/developer-doc/swap-api/reference/direct/get-info.md) — server info and settings types \* \[GetVenues / ListProviders\](/titan/developer-doc/swap-api/reference/direct/venues-providers.md) — discovery types \* \[StopStream\](/titan/developer-doc/swap-api/reference/direct/stop-stream.md) — stop stream types --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk.md). # SDK Reference \*\*Titan provides official SDKs for TypeScript and Rust.\*\* Both connect to Titan Direct over WebSocket using MessagePack encoding with optional compression. \*\*\* ## TypeScript SDK \*\*A high-level client with built-in connection management, compression negotiation, and full type safety.\*\* \* \*\*Package:\*\* \`@titanexchange/sdk-ts\` \* \*\*Source:\*\* \[github.com/Titan-Pathfinder/titan-sdk-ts\](https://github.com/Titan-Pathfinder/titan-sdk-ts) \* \*\*Node.js:\*\* >=18.19 ### Installation \`\`\`bash npm install @titanexchange/sdk-ts \`\`\` ### Connecting \`\`\`typescript import { V1Client } from '@titanexchange/sdk-ts'; // The SDK automatically negotiates compression (zstd, brotli, gzip, or none) const url = \`wss://${process.env.TITAN\_ENDPOINT}/ws?auth=${process.env.TITAN\_API\_KEY}\`; const client = await V1Client.connect(url); \`\`\` \*\*Connection state:\*\* \* \*\*\`client.closed\`\*\* — \`boolean\`, \`true\` if the connection is closed. \* \*\*\`client.listen\_closed()\`\*\* — Returns a \`Promise\` that resolves with the close event. \* \*\*\`client.close()\`\*\* — Gracefully closes the connection. ### API methods \* \*\*\`client.getInfo()\`\*\* → \`ServerInfo\` — Protocol version and server settings. \* \*\*\`client.newSwapQuoteStream(params)\`\*\* → \`{ response, stream, streamId }\` — Opens a streaming quote. \* \*\*\`client.stopStream(streamId)\`\*\* → \`StopStreamResponse\` — Stops a stream by ID. \* \*\*\`client.getVenues(params?)\`\*\* → \`VenueInfo\` — Lists available on-chain venues. \* \*\*\`client.listProviders(params?)\`\*\* → \`ProviderInfo\[\]\` — Lists active quote providers. \* \*\*\`client.getSwapPrice(params)\`\*\* → \`SwapPrice\` — One-shot price check without streaming. ### Streaming quotes \`\`\`typescript import bs58 from 'bs58'; const { stream, streamId } = await client.newSwapQuoteStream({ swap: { inputMint: bs58.decode('So11111111111111111111111111111111111111112'), outputMint: bs58.decode('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'), amount: 1\_000\_000\_000n, // 1 SOL — must be BigInt slippageBps: 50, }, transaction: { userPublicKey: bs58.decode('YOUR\_WALLET\_PUBLIC\_KEY'), }, }); // Async iterator — yields SwapQuotes on each update for await (const quotes of stream) { // Use metadata.ExpectedWinner for the best slippage-adjusted route const winner = quotes.metadata?.ExpectedWinner; const best = winner && quotes.quotes\[winner\]; if (best?.instructions?.length) { console.log(\`Best: ${winner} — ${best.outAmount} out\`); } } \`\`\` ### Stopping a stream \`\`\`typescript // Method 1: via client await client.stopStream(streamId); // Method 2: via stream — calls stopStream() internally await stream.cancel('done'); \`\`\` ### Types \`\`\`typescript import { types } from '@titanexchange/sdk-ts'; types.v1.SwapQuoteRequest; types.v1.SwapParams; types.v1.TransactionParams; \`\`\` ### Error handling All error classes are exported from the SDK: \* \*\*\`ConnectionClosed\`\*\* — WebSocket connection closed. Properties: \`code\`, \`reason\`, \`wasClean\`. \* \*\*\`ConnectionError\`\*\* — WebSocket error event. Property: \`cause\`. \* \*\*\`ErrorResponse\`\*\* — Server rejected a request. Property: \`response\` (with \`code\`, \`message\`, \`requestId\`). \* \*\*\`StreamError\`\*\* — Stream ended with an error. Properties: \`streamId\`, \`errorCode\`, \`errorMessage\`. \* \*\*\`ProtocolError\`\*\* — Implementation bug — \*\*report to Titan.\*\* Properties: \`reason\`, \`data\`. ### Reconnection \*\*The SDK does not include built-in reconnect logic.\*\* Handle reconnection manually: \`\`\`typescript client.listen\_closed().then(async (event) => { if (!event.wasClean) { // Reconnect and re-establish streams const newClient = await V1Client.connect(url); // Re-open your streams on newClient } }); \`\`\` See \[Error Handling & Reconnect\](/titan/developer-doc/swap-api/guides/error-handling.md) for backoff strategies. ### Browser usage \`\`\`typescript import { V1Client } from '@titanexchange/sdk-ts/browser'; \`\`\` {% hint style="warning" %} \*\*Do not expose your API key in client-side code.\*\* Use a middleware proxy that accepts user connections, validates authentication, and forwards to Titan with the API key server-side. See \`examples/middleware.ts\` in the SDK repository. {% endhint %} ### Key details \* \*\*BigInt amounts\*\* — Pass \`amount\` as \`BigInt\` (e.g. \`1\_000\_000\_000n\`). Numbers >= 2^32 may be encoded as float64, \*\*which the server rejects.\*\* \* \*\*\`quotes\` is a map\*\* — \`SwapQuotes.quotes\` is \`Record\`, keyed by provider ID. Not every provider appears in every update. \* \*\*\`num\_quotes\` uses snake\\\_case\*\* — In \`QuoteUpdateParams\`, the field is \`num\_quotes\` (not \`numQuotes\`). \*\*Logging BigInt values:\*\* \`\`\`typescript JSON.stringify(data, (key, value) => { if (typeof value === 'bigint') return value.toString() + 'n'; if (value instanceof Uint8Array) return \`\`; return value; }, 2); \`\`\` \*\*\* ## Rust SDK \*\*Low-level type definitions and MessagePack codec — you manage the WebSocket connection yourself.\*\* \* \*\*Crates:\*\* \[crates.io/search?q=titan-api-types\](https://crates.io/search?q=titan-api-types) \* \*\*\`titan-api-types\`\*\* — Type definitions for all WebSocket request and response messages. \* \*\*\`titan-api-codec\`\*\* — MessagePack encoding/decoding with compression support (zstd, brotli, gzip). ### Installation \`\`\`toml \[dependencies\] titan-api-types = "5" titan-api-codec = "1.2" \`\`\` {% hint style="info" %} There is \*\*no high-level client\*\* in the Rust SDK. You manage the WebSocket connection using \`tokio-tungstenite\` (or any async WebSocket library) and use the codec for serialization. {% endhint %} ### Connecting \`\`\`rust use titan\_api\_codec::codec::{ws::v1::ClientCodec, Codec}; use titan\_api\_types::ws::v1; use tokio\_tungstenite::{ connect\_async, tungstenite::{ client::IntoClientRequest, http::header::{AUTHORIZATION, SEC\_WEBSOCKET\_PROTOCOL}, http::HeaderValue, }, }; let mut request = url.into\_client\_request()?; // Set protocol negotiation header let protocols = HeaderValue::from\_str( &v1::WEBSOCKET\_SUBPROTOCOLS.join(", ") )?; request.headers\_mut().insert(SEC\_WEBSOCKET\_PROTOCOL, protocols); // Set auth header let bearer = HeaderValue::from\_str(&format!("Bearer {}", token))?; request.headers\_mut().insert(AUTHORIZATION, bearer); // Connect and create codec from negotiated protocol let (stream, response) = connect\_async(request).await?; let protocol\_str = response .headers() .get(SEC\_WEBSOCKET\_PROTOCOL) .and\_then(|v| v.to\_str().ok()) .unwrap(); let codec = ClientCodec::from\_str(protocol\_str)?; let (sink, stream) = stream.split(); \`\`\` ### Sending requests \`\`\`rust use titan\_api\_types::ws::v1::\*; use titan\_api\_codec::codec::Codec; let encoder = codec.encoder(); let decoder = codec.decoder(); // GetInfo request let request = ClientRequest { id: 0, data: RequestData::GetInfo(GetInfoRequest::default()), }; // Encode to MessagePack and send as binary frame let bytes = encoder.encode(&request)?; sink.send(Message::Binary(bytes)).await?; \`\`\` ### Streaming quotes \`\`\`rust let request = ClientRequest { id: 1, data: RequestData::NewSwapQuoteStream(SwapQuoteRequest { swap: SwapParams { input\_mint: sol\_mint, output\_mint: usdc\_mint, amount: 1\_000\_000\_000u64, slippage\_bps: Some(50), ..Default::default() }, transaction: TransactionParams { user\_public\_key: wallet, ..Default::default() }, update: None, }), }; \`\`\` ### Processing responses \`\`\`rust while let Some(msg) = stream.next().await { let msg = msg?; if let Message::Binary(data) = msg { let server\_msg: ServerMessage = decoder.decode(data.into())?; match server\_msg { ServerMessage::Response(resp) => { // Handle RPC response } ServerMessage::StreamData(data) => { if let StreamDataPayload::SwapQuotes(quotes) = data.payload { for (provider, route) in "es.quotes { println!("{}: {} out", provider, route.out\_amount); } } } ServerMessage::Error(err) => { eprintln!("Error {}: {}", err.code, err.message); } ServerMessage::StreamEnd(end) => { println!("Stream {} ended", end.id); } } } } \`\`\` ### Field naming The Rust SDK uses standard \*\*snake\\\_case\*\* field names. Serde handles the conversion to camelCase on the wire: \* \`input\_mint\` → \`inputMint\` \* \`output\_mint\` → \`outputMint\` \* \`slippage\_bps\` → \`slippageBps\` \* \`user\_public\_key\` → \`userPublicKey\` \* \`only\_direct\_routes\` → \`onlyDirectRoutes\` ### Reconnection \*\*No built-in reconnect logic.\*\* Handle connection drops and re-establish streams manually, same as the TypeScript SDK. ### Dependencies \* \*\*\`tokio-tungstenite\`\*\* — Async WebSocket client. \* \*\*\`rmp-serde\`\*\* — MessagePack serialization. \* \*\*\`zstd\`\*\* — Zstandard compression. \* \*\*\`brotli\`\*\* — Brotli compression. \* \*\*\`flate2\`\*\* — Gzip compression. \* \*\*\`five8\` / \`five8\_const\`\*\* — Base58 pubkey encoding. \*\*\* ## Related pages \* \[Quickstart\](/titan/developer-doc/swap-api/quickstart.md) — End-to-end integration example \* \[Stream & Execute a Swap\](/titan/developer-doc/swap-api/guides/stream-and-execute.md) — Full guide with transaction building \* \[Wire Protocol\](/titan/developer-doc/swap-api/reference/wire-protocol.md) — Encoding rules and message envelopes \* \[Error Codes\](/titan/developer-doc/swap-api/reference/error-codes.md) — Error codes and SDK error classes --- # Overview | Developer Docs | Titan For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt) . This page is also available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview.md) . **Titan's Limit Orders are designed to thrive in a competitive environment where searchers play a central role.** By participating as a searcher, you gain access to a marketplace of on-chain limit orders. Limit orders are resting on-chain and can be partially or completely filled. **Fees are charged to takers** and are charged as `output_mint` tokens. **Program address:** `TitanLozLMhczcwrioEguG2aAmiATAPXdYpBg3DbeKK` * * * Order structure[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders#order-structure) ---------------------------------------------------------------------------------------------------------------- Each limit order is a PDA derived from the maker's public key, input mint, output mint, and an order ID. Copy use pinocchio::pubkey::{create_program_address, Pubkey}; use bytemuck::{Pod, Zeroable}; /// Limit order structure #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] pub struct LimitOrder { // The public key of the order, pub maker: Pubkey, // Input mint of the limit order pub input_mint: Pubkey, // Output mint of the limit order pub output_mint: Pubkey, // Slot which the order was created pub creation_slot: u64, // The slot at which the order expires pub expiration_slot: u64, // The amount of input tokens to be exchanged pub amount: u64, // The amount of input tokens that have been filled pub amount_filled: u64, // The amount of output tokens that have been exchanged. pub out_amount_filled: u64, // The amount of output tokens that the maker has withdrawn. pub out_amount_withdrawn: u64, // The amount of fees paid in the smallest unit of from_token mint. pub fees_paid: u64, // Price base in the order, in the smallest unit of output token pub price_base: u64, // Price exponent, price is calculated as price_base * 10^(-price_exponent) pub price_exponent: u8, // The status of the order pub status: u8, // Bump seed for the limit order PDA pub bump: u8, // Unique identifier for the order, used to differentiate orders for same // (owner, input_mint, output_mint) tuple pub id: u8, // Bump seed for the input mint vault PDA pub input_mint_vault_bump: u8, // Bump seed for the output mint vault PDA pub output_mint_vault_bump: u8, // Time in order pub time_in_force: u8, // Fees ticks rate for the order from takers. pub fee_ticks: u8, } **PDA seeds:** `["order", maker, input_mint, output_mint, id, bump]` **Account size:** 168 bytes. ### PDA derivation[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders#pda-derivation) Copy impl LimitOrder { pub const SEEDS: &'static [u8] = b"order"; pub const LEN: usize = 168; /// Get the pda address for the limit order, given the maker, input mint, /// output mint, id and bump. pub fn get_pda_address( maker: &Pubkey, input_mint: &Pubkey, output_mint: &Pubkey, id: u8, bump: u8, ) -> Result { let b0 = &[id]; let b1 = &[bump]; let seeds_with_bump = [\ LimitOrder::SEEDS,\ maker.as_ref(),\ input_mint.as_ref(),\ output_mint.as_ref(),\ b0,\ b1,\ ] .to_vec(); create_program_address(&seeds_with_bump, &crate::ID) } } * * * Price calculation[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders#price-calculation) -------------------------------------------------------------------------------------------------------------------- Price is stored as `price_base * 10^(-price_exponent)`. For example, a 100 USDC → 1 SOL order uses `price_base = 1` and `price_exponent = 2`, giving a price of `0.01` output tokens per input token. Copy impl LimitOrder { /// Calculate the costs and fees for a given amount and fee ticks. pub fn calculate_costs_and_fee( &self, amount: u64, fee_ticks: u8, ) -> Result<(u64, u64), ProgramError> { let amount_u128 = amount as u128; let price_base = self.price_base as u128; let price_exponent = 10u128.pow(self.price_exponent as u32); let fee_units_u128 = (fee_ticks as u16).saturating_mul(FEE_TICK_UNITS as u16) as u128; // Calculate the transfer amount and fee amount. // Should never overflow, since its u64 * u64 // Use method to perform ceiling math division: (a + b - 1) / b let cost_u128 = amount_u128 .saturating_mul(price_base) .checked_add(price_exponent.saturating_sub(1)) .ok_or(ProgramError::ArithmeticOverflow)? .saturating_div(price_exponent); let cost: u64 = cost_u128 .try_into() .map_err(|_| ProgramError::ArithmeticOverflow)?; let fees: u64 = cost_u128 .saturating_mul(fee_units_u128) .saturating_div(FEE_TICK_DIVISOR) .try_into() .map_err(|_| ProgramError::ArithmeticOverflow)?; Ok((cost, fees.max(1))) } /// Amount left to be filled in the order. pub fn get_remaining_amount(&self) -> u64 { self.amount.saturating_sub(self.amount_filled) } } * * * Fees[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders#fees) ------------------------------------------------------------------------------------------ Fees are charged to takers in the **output token**. The fee rate is stored as `fee_ticks` on the order. Copy fee_units = fee_ticks × 25 fee = cost × fee_units / 1,000,000 **The minimum fee is always 1 unit of the output token.** Copy /// Fee tick units pub const FEE_TICK_UNITS: u8 = 25; /// 1e6 units = 0.0001, used to convert fee tick rate to fee basis points pub const FEE_TICK_DIVISOR: u128 = 1_000_000; **Fee receiver address:** `Bq5ZzfiU3vTiJPrBJFcr98BnUy9Wc1dg9ASeycB2tX1C` * * * Time-in-force[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders#time-in-force) ------------------------------------------------------------------------------------------------------------ Each order carries a time-in-force policy that controls fill behavior. Copy /// Time in Force (TIF) for limit orders. Provides different behaviors for /// how long an order remains active and how it can be filled. #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq)] pub enum TimeInForce { /// Order is good until cancelled. Partial takes are allowed. GoodTillCancelled = 0, /// After taking any amount, order is closed. TakeCancelsOrder = 1, /// Takes must completely fill the order. AllOrNothing = 2, /// Same as TakeCancelsOrder but it must be filled at the same time of creation. ImmediateOrCancel = 3, /// Same as AllOrNothing but it must be filled at the same time of creation. FillOrKill = 4, } * `**GoodTillCancelled**` **(0)** — Remains open until fully filled or cancelled. **Partial fills allowed.** * `**TakeCancelsOrder**` **(1)** — Closes after any take, regardless of fill amount. * `**AllOrNothing**` **(2)** — Takes must completely fill the remaining amount. * `**ImmediateOrCancel**` **(3)** — Same as `TakeCancelsOrder`, but **must be filled in the same slot as creation.** * `**FillOrKill**` **(4)** — Same as `AllOrNothing`, but **must be filled in the same slot as creation.** * * * Order status[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders#order-status) ---------------------------------------------------------------------------------------------------------- Copy /// Order status for limit orders. Indicates the current state of the order /// and how it can be interacted with. #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq)] pub enum OrderStatus { /// Order is open, can be partially filled, filled, cancelled Open = 0, /// Order is partially filled, can be filled or cancelled PartiallyFilled = 1, /// Order is filled, terminates, used for event logging Filled = 2, /// Order is cancelled, terminates, used for event logging Cancelled = 3, } * `**Open**` **(0)** — Can be partially filled, fully filled, or cancelled. * `**PartiallyFilled**` **(1)** — Some amount filled. Can still be filled or cancelled. * `**Filled**` **(2)** — Fully filled. **Terminal state.** * `**Cancelled**` **(3)** — Cancelled by maker. **Terminal state.** * * * Related pages[](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders#related-pages) ------------------------------------------------------------------------------------------------------------ * [Placing Taker Orders](https://github.com/Titan-Pathfinder/titan-gitbook-doc/blob/main/limit-orders/take-order.md) — TakeOrder instruction, accounts, WSOL handling, and full execution code * [Limit Order Events](https://github.com/Titan-Pathfinder/titan-gitbook-doc/blob/main/limit-orders/events.md) — Event structure and parsing from program logs * [Error Codes](https://github.com/Titan-Pathfinder/titan-gitbook-doc/blob/main/limit-orders/error-codes.md) — Program error codes [PreviousLimits & Idempotency](https://titan-exchange.gitbook.io/titan/developer-doc/dca-partner-api/reference/limits) [NextPlacing Taker Orders](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/take-order) Last updated 4 months ago ---