From e6ef3fe7d7a14c4a8021de2e7f745afda000d1c8 Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 21:36:10 +0000 Subject: [PATCH 1/6] Updated mintlify pages - Updated welcome.mdx - Created untitled-page.mdx - Created untitled-page-2.mdx - Created untitled-page-4.mdx - Created untitled-page-3.mdx - Created untitled-page-5.mdx - Created snippets/shared/balance-concepts-2.mdx - Updated docs.json Mintlify-Source: dashboard-editor --- docs.json | 13 +- snippets/shared/balance-concepts-2.mdx | 7 + untitled-page-2.mdx | 133 ++++ untitled-page-3.mdx | 179 ++++++ untitled-page-4.mdx | 142 +++++ untitled-page-5.mdx | 133 ++++ untitled-page.mdx | 101 +++ welcome.mdx | 823 +++++++++++++------------ 8 files changed, 1120 insertions(+), 411 deletions(-) create mode 100644 snippets/shared/balance-concepts-2.mdx create mode 100644 untitled-page-2.mdx create mode 100644 untitled-page-3.mdx create mode 100644 untitled-page-4.mdx create mode 100644 untitled-page-5.mdx create mode 100644 untitled-page.mdx diff --git a/docs.json b/docs.json index 3f67ea78..b55536a8 100644 --- a/docs.json +++ b/docs.json @@ -443,7 +443,18 @@ ] }, "features/transaction-management/balances", - "features/transaction-management/fiat-on-ramp" + "features/transaction-management/fiat-on-ramp", + { + "group": "Swaps", + "tag": "Early Access", + "pages": [ + "untitled-page", + "untitled-page-2", + "untitled-page-4", + "untitled-page-3", + "untitled-page-5" + ] + } ] }, { diff --git a/snippets/shared/balance-concepts-2.mdx b/snippets/shared/balance-concepts-2.mdx new file mode 100644 index 00000000..c82c338a --- /dev/null +++ b/snippets/shared/balance-concepts-2.mdx @@ -0,0 +1,7 @@ +--- +title: "swaps-beta-note.mdx" +--- + + + Swaps are in early access. [Contact us](https://www.turnkey.com/contact-us) to enable it for your organization. + \ No newline at end of file diff --git a/untitled-page-2.mdx b/untitled-page-2.mdx new file mode 100644 index 00000000..97dc7e9e --- /dev/null +++ b/untitled-page-2.mdx @@ -0,0 +1,133 @@ +--- +title: "Enable Swaps" +description: "One-time parent-organization activity that enables swaps and sets your integrator fee configuration." +mode: "wide" +noindex: true +--- + +import SwapBetaNote from "/snippets/shared/swaps-beta-note.mdx"; + +`ACTIVITY_TYPE_ENABLE_SWAP` is a parent-organization configuration activity that writes `ORG_FEATURE_SWAP_CONFIG` — the source of truth for whether swaps are enabled, your integrator fee, and the wallet that receives fees. Enable must complete before any sub-organization can call `get_swap_quote` or `execute_swap`. + +## Prerequisites + +- The activity is submitted **against the parent organization**. Sub-organization requests are rejected. +- You control a wallet that will receive integrator fees. It must be a valid address for the target route and provider _(To Confirm — likely any EVM address in V1)_. +- You've decided on your fee rate in basis points. See [Choose your fee configuration](#choose-your-fee-configuration). + +## Submit enable\_swap + + + `ACTIVITY_TYPE_ENABLE_SWAP` + + + + Timestamp (in milliseconds) of the request, used to verify liveness. + + + + Unique identifier of the **parent** organization. Requests from sub-organizations are rejected. + + + + Whether quote and execute activities are permitted. Set `true` to enable, `false` to disable. + + + + The wallet address that receives integrator fees on-chain. Must be valid for the route and provider _(To Confirm — likely any EVM address in V1)_. + + + + Your integrator fee in basis points, expressed as a stringified integer. For example, `"50"` = 0.5%. Must be non-negative and no greater than the Turnkey-defined maximum. + + +```bash title="cURL" +curl --request POST \ + --url https://api.turnkey.com/public/v1/submit/enable_swap \ + --header 'Accept: application/json' \ + --header 'Content-Type: application/json' \ + --header "X-Stamp: (see Stamps)" \ + --data '{ + "type": "ACTIVITY_TYPE_ENABLE_SWAP", + "timestampMs": " (e.g. 1745474677453)", + "organizationId": "", + "parameters": { + "enabled": true, + "feeReceiverWalletAddress": "", + "feeBps": "50" + } + }' +``` + +```javascript title="JavaScript" +import { TurnkeyClient } from "@turnkey/http"; +import { ApiKeyStamper } from "@turnkey/api-key-stamper"; + +const client = new TurnkeyClient( + { baseUrl: "https://api.turnkey.com" }, + new ApiKeyStamper({ + apiPublicKey: process.env.TURNKEY_API_PUBLIC_KEY, + apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY, + }), +); + +const { activity } = await client.request("/public/v1/submit/enable_swap", { + type: "ACTIVITY_TYPE_ENABLE_SWAP", + timestampMs: String(Date.now()), + organizationId: "", + parameters: { + enabled: true, + feeReceiverWalletAddress: "", + feeBps: "50", + }, +}); +``` + +`enable_swap` completes inline. The activity result echoes the applied configuration: + +```json +{ + "activity": { + "id": "", + "status": "ACTIVITY_STATUS_COMPLETED", + "type": "ACTIVITY_TYPE_ENABLE_SWAP", + "result": { + "enableSwapResult": { + "config": { + "enabled": true, + "feeReceiverWalletAddress": "", + "feeBps": "50" + } + } + } + } +} +``` + +_(Response shape `To Confirm` — assumes `enableSwapResult.config` echoes the applied configuration for confirmation.)_ + +## Choose your fee configuration + +Your `feeBps` applies to every swap submitted under any sub-organization of your parent org. Two constraints to keep in mind: + +- **Cap**: Turnkey enforces a maximum `feeBps`. Requests above the cap are rejected. The current maximum is documented in the API reference and may be adjusted over time _(To Confirm — current cap value)_. +- **Fee currency**: fees accrue on-chain to `feeReceiverWalletAddress` in the token that the swap route settles the fee in _(To Confirm — 0x currently collects the integrator fee in the sell token; document once verified)_. + +Pick a `feeBps` that reflects your intended margin and confirm the receiver wallet is one you actively control. Fees paid to a wallet you don't control cannot be recovered by Turnkey. + +## Change your fee configuration + +Configuration changes go through the same `ACTIVITY_TYPE_ENABLE_SWAP` activity — submit again with the updated `feeBps`, `feeReceiverWalletAddress`, or `enabled` flag. There is no separate update activity. + +Quote and execute activities submitted **after** the change immediately use the new configuration. Because executable provider data is fetched fresh during `execute_swap`, an in-flight quote followed by an execute will settle at the fee configuration active at execute time, not at quote time _(To Confirm)_. + +To disable swaps entirely, submit `enable_swap` with `enabled: false`. + + + Disabling swaps rejects all subsequent `get_swap_quote` and `execute_swap` requests until re-enabled. Wallets are unaffected — nothing on-chain changes. + + +## Next steps + +- [Get a quote](/features/transaction-management/swap/get-swap-quote) — fee-aware indicative pricing for display and selection +- [Execute a swap](/features/transaction-management/swap/execute-swap) — the signing activity that runs the swap end-to-end \ No newline at end of file diff --git a/untitled-page-3.mdx b/untitled-page-3.mdx new file mode 100644 index 00000000..c428693b --- /dev/null +++ b/untitled-page-3.mdx @@ -0,0 +1,179 @@ +--- +title: "Executing Swaps" +description: "Sign, broadcast, and settle a token swap from a Turnkey wallet. Handle quote refresh, ERC-20 approvals, and (optionally) gas sponsorship internally." +mode: "wide" +noindex: true +--- + +import SwapBetaNote from "/snippets/shared/swaps-beta-note.mdx"; + +`ACTIVITY_TYPE_EXECUTE_SWAP` is the signing activity that runs a swap end-to-end. The caller signs a swap intent. Turnkey fetches a fresh executable quote from the provider, constructs the transaction (batching any required ERC-20 approvals via Permit2), signs it with the wallet in the intent, and broadcasts. Policy engines evaluate the derived transaction, so existing wallet policies apply to the resulting on-chain transfer. + + + +## Prerequisites + +- The parent organization has [enabled swaps](/features/transaction-management/swap/enable-swap). Execute requests against orgs with swaps disabled are rejected. +- The `walletAccount` in the intent holds enough of the input token to cover `inputAmount`. For non-sponsored swaps it also needs the origin chain's native token for gas. +- For cross-chain routes, the destination chain is supported by the provider. See [Supported providers, chains, and routes](/features/transaction-management/swap#supported-providers-chains-and-routes). + +## Submit execute\_swap + + + `ACTIVITY_TYPE_EXECUTE_SWAP` + + + + Timestamp (in milliseconds) of the request, used to verify liveness. + + + + Unique identifier of the sub-organization executing the swap. Fee configuration is loaded from the parent organization server-side. + + + + CAIP-19 identifier for the token being sold. Example: `eip155:8453/erc20:0x833589fCD6EDB6E08f4c7C32D4f71b54bdA02913` (USDC on Base). The origin chain is derived from this identifier. + + + + CAIP-19 identifier for the token being bought. Example: `eip155:8453/erc20:0x4200000000000000000000000000000000000006` (WETH on Base). Same-chain if the CAIP-2 prefix matches `inputToken`. Otherwise a cross-chain route. + + + + Amount of the input token, in raw on-chain units. + + + + The wallet account address to sign with and swap from. Part of the user-signed intent. Authentication identifies the caller. It does not determine the signing wallet. + + + + Slippage tolerance in basis points, expressed as a stringified integer. Enforced at execute time as protection against market movement between quote fetch and settlement. + + + + Whether to sponsor the transaction's gas via Gas Station. Defaults to `false`. Requires a Pro plan or higher when set to `true`. _(To Confirm: plan gating.)_ + + + + +```bash title="cURL" +curl --request POST \ + --url https://api.turnkey.com/public/v1/submit/execute_swap \ + --header 'Accept: application/json' \ + --header 'Content-Type: application/json' \ + --header "X-Stamp: (see Stamps)" \ + --data '{ + "type": "ACTIVITY_TYPE_EXECUTE_SWAP", + "timestampMs": " (e.g. 1745474677453)", + "organizationId": "", + "parameters": { + "inputToken": "eip155:8453/erc20:0x833589fCD6EDB6E08f4c7C32D4f71b54bdA02913", + "outputToken": "eip155:8453/erc20:0x4200000000000000000000000000000000000006", + "inputAmount": "1000000", + "walletAccount": "", + "slippage": "50", + "sponsor": false + } + }' +``` + +```javascript title="JavaScript" +import { TurnkeyClient } from "@turnkey/http"; +import { ApiKeyStamper } from "@turnkey/api-key-stamper"; + +const client = new TurnkeyClient( + { baseUrl: "https://api.turnkey.com" }, + new ApiKeyStamper({ + apiPublicKey: process.env.TURNKEY_API_PUBLIC_KEY, + apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY, + }), +); + +const { activity } = await client.request("/public/v1/submit/execute_swap", { + type: "ACTIVITY_TYPE_EXECUTE_SWAP", + timestampMs: String(Date.now()), + organizationId: "", + parameters: { + inputToken: "eip155:8453/erc20:0x833589fCD6EDB6E08f4c7C32D4f71b54bdA02913", + outputToken: "eip155:8453/erc20:0x4200000000000000000000000000000000000006", + inputAmount: "1000000", + walletAccount: "", + slippage: "50", + sponsor: false, + }, +}); +``` + + + +The activity result returns a poll handle: + +```json +{ + "activity": { + "id": "", + "status": "ACTIVITY_STATUS_COMPLETED", + "type": "ACTIVITY_TYPE_EXECUTE_SWAP", + "result": { + "executeSwapResult": { + "sendTransactionStatusId": "" + } + } + } +} +``` + +_(Field name `sendTransactionStatusId` `To Confirm`. Response shape mirrors other send-transaction activities.)_ + +## Gas: sponsored vs. self-funded + +With `sponsor: true`, Gas Station pays the gas and the batch executes as an EIP-7702 sponsored transaction. The `walletAccount` needs no native token at all. This requires a Pro plan or higher. _(To Confirm.)_ + +With `sponsor: false`, the `walletAccount` pays gas itself. Fund it with the origin chain's native token before executing. + +## What the caller does not provide + +The user-signed intent covers only the swap parameters listed above. Turnkey generates or fetches the rest server-side and never accepts them from callers: + +- Provider quote JSON +- Transaction calldata +- Serialized transactions +- Permit2 payloads +- Approval payloads +- Fee receiver wallet address (loaded from parent org) +- Fee bps (loaded from parent org) + +This is a hard trust boundary. Requests that include provider payloads or fee overrides are rejected. + +## Approvals and Permit2 + +For ERC-20 swaps, Turnkey batches token approvals into `execute_swap` using Permit2. There is no separate approval activity for customers to run. Permit2 signatures are generated inside the activity and included in the transaction as needed. + +Native token swaps (e.g., ETH as `inputToken`) bypass Permit2 and require no approval step. + + + Some non-standard ERC-20 tokens may fail Permit2 approval flows. If a swap fails with a Permit2-related error, verify the token supports the standard Permit2 flow before retrying. + + +## Same-chain vs. cross-chain execution + +The execute call itself is identical for same-chain and cross-chain routes. The provider selects the route from the CAIP-19 pair. After broadcast, the lifecycle diverges: + +- **Same-chain swaps** complete on origin inclusion. Poll `get_send_transaction_status` with the returned handle. +- **Cross-chain swaps** continue past origin inclusion until the destination-chain leg settles or funds are recovered. Poll `get_swap_status` for the full lifecycle. + +See [Track swap status](/features/transaction-management/swap/track-swap-status) for the two lifecycle models. + +## Poll swap status (required) + + + `ACTIVITY_STATUS_COMPLETED` on the execute response means the activity was accepted and enqueued for broadcast. It does not mean the transaction landed on-chain. Poll the appropriate status endpoint until it reports a terminal state. + + +For same-chain, poll `get_send_transaction_status`. For cross-chain, poll `get_swap_status`. Full lifecycle semantics on [Track swap status](/features/transaction-management/swap/track-swap-status). + +## Next steps + +- [Track swap status](/features/transaction-management/swap/track-swap-status): same-chain vs. cross-chain polling. +- [End-to-end example](/features/transaction-management/swap/end-to-end-example): full flow from enable to confirmation. \ No newline at end of file diff --git a/untitled-page-4.mdx b/untitled-page-4.mdx new file mode 100644 index 00000000..b669e255 --- /dev/null +++ b/untitled-page-4.mdx @@ -0,0 +1,142 @@ +--- +title: "Getting a Quote" +description: "Fetch a fee-aware indicative quote for a token pair. Displays expected output and the minimum output the caller should accept." +mode: "wide" +noindex: true +--- + +## Prerequisites + +- The parent organization has [enabled swaps](/features/transaction-management/swap/enable-swap) with a valid `feeReceiverWalletAddress` and `feeBps`. Quote requests against orgs with swaps disabled are rejected. +- The token pair maps to a supported provider route (0x in V1). Same-chain and EVM ↔ EVM cross-chain routes are supported. + +## Submit get\_swap\_quote + + + `ACTIVITY_TYPE_GET_SWAP_QUOTE` + + + + Timestamp (in milliseconds) of the request, used to verify liveness. + + + + Unique identifier of the sub-organization requesting the quote. The parent organization's active `ORG_FEATURE_SWAP_CONFIG` is applied server-side. + + + + CAIP-19 identifier for the token being sold. Example: `eip155:8453/erc20:0x833589fCD6EDB6E08f4c7C32D4f71b54bdA02913` (USDC on Base). The origin chain is derived from this identifier. + + + + CAIP-19 identifier for the token being bought. Example: `eip155:8453/erc20:0x4200000000000000000000000000000000000006` (WETH on Base). Same-chain if the CAIP-2 prefix matches `inputToken`. Otherwise a cross-chain route. + + + + Amount of the input token, in raw on-chain units. For example, `"1000000"` for 1 USDC at 6 decimals. + + + + Slippage tolerance in basis points, expressed as a stringified integer. For example, `"50"` = 0.5%. Applied to `minOutputAmount` computation. _(To Confirm: required vs. optional, and whether a server default applies if omitted.)_ + + +```bash title="cURL" +curl --request POST \ + --url https://api.turnkey.com/public/v1/submit/get_swap_quote \ + --header 'Accept: application/json' \ + --header 'Content-Type: application/json' \ + --header "X-Stamp: (see Stamps)" \ + --data '{ + "type": "ACTIVITY_TYPE_GET_SWAP_QUOTE", + "timestampMs": " (e.g. 1745474677453)", + "organizationId": "", + "parameters": { + "inputToken": "eip155:8453/erc20:0x833589fCD6EDB6E08f4c7C32D4f71b54bdA02913", + "outputToken": "eip155:8453/erc20:0x4200000000000000000000000000000000000006", + "inputAmount": "1000000", + "slippage": "50" + } + }' +``` + +```javascript title="JavaScript" +import { TurnkeyClient } from "@turnkey/http"; +import { ApiKeyStamper } from "@turnkey/api-key-stamper"; + +const client = new TurnkeyClient( + { baseUrl: "https://api.turnkey.com" }, + new ApiKeyStamper({ + apiPublicKey: process.env.TURNKEY_API_PUBLIC_KEY, + apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY, + }), +); + +const { activity } = await client.request("/public/v1/submit/get_swap_quote", { + type: "ACTIVITY_TYPE_GET_SWAP_QUOTE", + timestampMs: String(Date.now()), + organizationId: "", + parameters: { + inputToken: "eip155:8453/erc20:0x833589fCD6EDB6E08f4c7C32D4f71b54bdA02913", + outputToken: "eip155:8453/erc20:0x4200000000000000000000000000000000000006", + inputAmount: "1000000", + slippage: "50", + }, +}); +``` + +Response: + +```json +{ + "activity": { + "id": "", + "status": "ACTIVITY_STATUS_COMPLETED", + "type": "ACTIVITY_TYPE_GET_SWAP_QUOTE", + "result": { + "getSwapQuoteResult": { + "quoteId": "", + "inputToken": "eip155:8453/erc20:0x833589fCD6EDB6E08f4c7C32D4f71b54bdA02913", + "outputToken": "eip155:8453/erc20:0x4200000000000000000000000000000000000006", + "inputAmount": "1000000", + "outputAmount": "", + "minOutputAmount": "" + } + } + } +} +``` + +_(Response shape `To Confirm`. Field name `getSwapQuoteResult` follows Turnkey activity conventions.)_ + +### Response fields + +- `quoteId`: opaque identifier for the quote. Not required for execution. Useful for logging and correlation. +- `inputToken`, `outputToken`, `inputAmount`: echoed from the request for confirmation. +- `outputAmount`: expected output amount, fee-adjusted, in raw on-chain units. +- `minOutputAmount`: minimum output the caller should accept at execute time, given the requested slippage. Derived from `outputAmount` and the caller's slippage tolerance. + +## Quotes are indicative + +Quote outputs may differ from execution outputs. When you call `execute_swap`, Turnkey fetches fresh executable transaction data from the provider (not the quote's payload) and settles at market rates within the caller's slippage tolerance. Two implications: + +- Quote `outputAmount` is a hint. Actual receive amount at execute time can be higher or lower, bounded on the low side by the slippage-derived floor. +- Quotes are not persisted. Turnkey does not maintain a mapping from `quoteId` to executable data. The `quoteId` is opaque and cannot be replayed. + +If you display quote amounts to end users, refresh the quote near the moment of execution to keep the displayed value close to the settled value. + +## How fees are applied + +The `outputAmount` returned in a quote already reflects your organization's `feeBps` from `ORG_FEATURE_SWAP_CONFIG`. There is no separate fee amount to add or subtract on the client. The end user's displayed receive amount is the quote's `outputAmount` (or the actual settled amount at execute time). + +Fee configuration is snapshotted server-side per activity. If your fee configuration changes between quote and execute, the execute settles at the fee configuration active when it runs, not when the quote was fetched. _(To Confirm.)_ + +## Same-chain vs. cross-chain quotes + +Quote shape is identical for same-chain and cross-chain routes. The provider selects the route based on the `inputToken` and `outputToken` CAIP-19 identifiers. + +Cross-chain quotes are subject to the same freshness and slippage semantics as same-chain quotes, but cross-chain execution has a longer post-broadcast lifecycle. See [Track swap status](/features/transaction-management/swap/track-swap-status) for the two lifecycle models. + +## Next steps + +- [Execute a swap](/features/transaction-management/swap/execute-swap): the signing activity that runs the swap end-to-end. +- [Track swap status](/features/transaction-management/swap/track-swap-status): same-chain vs. cross-chain polling. \ No newline at end of file diff --git a/untitled-page-5.mdx b/untitled-page-5.mdx new file mode 100644 index 00000000..71c3975c --- /dev/null +++ b/untitled-page-5.mdx @@ -0,0 +1,133 @@ +--- +title: "Track Swap Status" +description: "Poll swap lifecycle from broadcast to settlement using get_swap_status. Same-chain and cross-chain operations normalize to a single PENDING/COMPLETED/FAILED model." +mode: "wide" +noindex: true +--- + +import SwapBetaNote from "/snippets/shared/swaps-beta-note.mdx"; + +`execute_swap` returns a poll handle. Query `get_swap_status` with that handle to observe the full lifecycle. Same-chain and cross-chain swaps share the same endpoint and the same response shape. The endpoint normalizes provider-specific state machines to three states: `PENDING`, `COMPLETED`, `FAILED`. + +## Lifecycle model + +Turnkey normalizes swap lifecycle to three states across same-chain and cross-chain routes: + +- **PENDING**: still in flight. Broadcast pending, origin transaction not yet included, or (cross-chain) destination-side execution not yet settled. +- **COMPLETED**: the user received the destination asset. Terminal, happy path. +- **FAILED**: the swap will not fill as intended. Reported only when Turnkey knows what the user holds. Terminal. + +Intermediate provider states (`bridge_pending`, `delayed`, `submitted`, and so on) collapse into `PENDING` on purpose. Every state maps to exactly one customer action: `PENDING` means wait, `COMPLETED` means you got the output, `FAILED` means look at what you hold and consider re-swapping. + +## Query get\_swap\_status + + + Unique identifier of the sub-organization that executed the swap. + + + + The poll handle returned from `execute_swap`. + + +```bash title="cURL" +curl --request POST \ + --url https://api.turnkey.com/public/v1/query/get_swap_status \ + --header 'Accept: application/json' \ + --header 'Content-Type: application/json' \ + --header "X-Stamp: (see Stamps)" \ + --data '{ + "organizationId": "", + "sendTransactionStatusId": "" + }' +``` + +```javascript title="JavaScript" +const status = await client.request("/public/v1/query/get_swap_status", { + organizationId: "", + sendTransactionStatusId: "", +}); +``` + +Response: + +```json +{ + "status": "PENDING | COMPLETED | FAILED", + "swapKind": "same_chain | cross_chain", + "provider": "zero_x", + "inputToken": "", + "outputToken": "", + "inputAmount": "", + "quotedOutputAmount": "", + "minOutputAmount": "", + "outputAmount": "", + "destinationTxHash": "", + "settledAsset": "", + "settledAmount": "", + "settlementTxHash": "", + "providerStatus": "", + "updatedAt": "" +} +``` + +_(Field names and JSON casing `To Confirm`.)_ + +### Response fields + +- `status`: normalized state. `PENDING`, `COMPLETED`, or `FAILED`. +- `swapKind`: `same_chain` or `cross_chain`. Set at execute time; does not change. +- `provider`: the provider that executed the swap. Currently `zero_x` in V1. +- `inputToken`, `outputToken`, `inputAmount`: echoed from the execute intent. +- `quotedOutputAmount`, `minOutputAmount`: from the quote. Always present. `minOutputAmount` is a guaranteed floor. +- `outputAmount`: actual amount received, on `COMPLETED`. May lag the `status` transition by seconds while the monitor fetches the final settled amount. +- `destinationTxHash`: destination-chain transaction on `COMPLETED`. Cross-chain only. +- `settledAsset`, `settledAmount`, `settlementTxHash`: on `FAILED`. See [What FAILED means](#what-failed-means). +- `providerStatus`: raw provider status passthrough for UI use. Cross-chain only. Not normative. +- `updatedAt`: last observed state change. + +## Same-chain vs. cross-chain differences + +The response shape is identical. Only two behaviors differ in practice: + +- **Timing.** Same-chain reaches a terminal state within one block time. Cross-chain can take tens of seconds to several minutes depending on the route and bridge. +- **Field population.** `destinationTxHash` and `providerStatus` are cross-chain only. On same-chain `FAILED`, the settled triple derives directly from the origin transaction: `settledAsset = inputToken`, `settledAmount = inputAmount`, `settlementTxHash = ` (nothing moved on-chain in a swap sense). + +Use `swapKind` in the response to branch client-side handling only when it matters. + +## What FAILED means + +`FAILED` is reported only when Turnkey knows what the user holds. A bridge failure with a refund still in flight stays `PENDING` until the refund lands (or is confirmed impossible). + +On `FAILED`, three fields describe the outcome: + +- `settledAsset` (CAIP-19): the asset the user ended up with. +- `settledAmount`: raw on-chain amount of that asset. +- `settlementTxHash`: the on-chain transaction that settled the funds. + +Provider-specific recovery scenarios normalize to this triple: + +- **Provider refunded on the origin chain**: `settledAsset` is the origin-chain refund token. It may differ from the original input token if the route included an origin-side swap. +- **Funds never left the user** (e.g., origin transaction reverted): `settledAsset` equals `inputToken`, `settledAmount` equals `inputAmount`, `settlementTxHash` is the origin transaction hash. +- **Nothing was recoverable**: `settledAsset` is `null`. Internal alerts fire on this state. + +If `settledAsset` is non-null, you can call [`execute_swap`](/features/transaction-management/swap/execute-swap) again with the settled asset as the new input to re-attempt. Caveats worth surfacing to end users: + +- The refunded amount is reduced by refund gas and any origin-side swap losses. +- A re-swap pays its own fees and slippage. +- If the refund landed on the origin chain and the original destination was on a different chain, the re-swap is again cross-chain. + + + `FAILED` with `settledAsset: null` means Turnkey could not recover funds through the provider's automated flows. Contact support if you encounter this state. + + +## Polling cadence + +- **Same-chain**: poll every ~1 second while `PENDING`. Typically settles within one block time. +- **Cross-chain**: poll every 5 to 10 seconds while `PENDING`. Settlement can take from tens of seconds to several minutes depending on route. + +Websocket and event-driven updates are on the roadmap and will replace polling for cross-chain lifecycles. + +## Next steps + +- [End-to-end example](/features/transaction-management/swap/end-to-end-example): full flow from enable to confirmation. +- [Execute a swap](/features/transaction-management/swap/execute-swap): request semantics that generate the poll handle. \ No newline at end of file diff --git a/untitled-page.mdx b/untitled-page.mdx new file mode 100644 index 00000000..9ee356c4 --- /dev/null +++ b/untitled-page.mdx @@ -0,0 +1,101 @@ +--- +title: "Swaps" +description: "Execute token swaps from Turnkey wallets through a unified DEX aggregator abstraction, with on-chain integrator fees for your organization." +mode: "wide" +noindex: true +--- + +import SwapBetaNote from "/snippets/shared/swaps-beta-note.mdx"; + +Swap lets wallets in your organization exchange tokens through DEX aggregators via a single Turnkey activity, with the usual audit trail and policy controls. Same-chain and EVM ↔ EVM cross-chain routes are supported. You can take an integrator fee on every swap, paid on-chain to a wallet you control. + + + +## What are Swaps + +Swaps connects Turnkey wallets to DEX aggregators through a unified interface. 0x is supported today; additional providers are on the roadmap. Callers submit signed intents describing what they want to swap while Turnkey handles quote fetching, transaction construction, ERC-20 approvals via Permit2, and broadcast. Callers never see provider quote payloads, transaction calldata, or approval data. + +Swaps enablement is a one-time setup step per parent organization. After that, each swap is a signed intent and a poll. + +## How it works + +1. Enable swaps once on your parent organization via [`enable_swap`](/features/transaction-management/swap/enable-swap), setting your integrator fee (bps) and fee-receiver wallet. +2. Optionally call [`get_swap_quote`](/features/transaction-management/swap/get-swap-quote) for an indicative quote to display to users. Quotes reflect your active fee configuration. +3. Submit [`execute_swap`](/features/transaction-management/swap/execute-swap) from the sub-organization whose wallet is swapping. Turnkey handles quote refresh, Permit2 approvals, and transaction construction internally. For cross-chain routes, the origin-chain transaction is broadcast here. +4. Poll swap status until settlement. Same-chain swaps complete on origin inclusion; cross-chain swaps have a longer lifecycle that continues until the destination-chain leg settles or funds are recovered. See [Track swap status](/features/transaction-management/swap/track-swap-status). + +## Supported providers, chains, and routes + +| Provider | Chains (V1) | +| :-- | :-- | +| 0x | Ethereum (`eip155:1`), Base (`eip155:8453`), Arbitrum (`eip155:42161`), Polygon (`eip155:137`), BNB Chain (`eip155:56`) | + +**Route support in V1:** + +- **Same-chain EVM ↔ EVM** — e.g., USDC → USDT on Base. +- **Cross-chain EVM ↔ EVM** — e.g., Arbitrum USDC → Base USDC, where supported by the provider. + +**On the roadmap:** Solana ↔ Solana, EVM ↔ SVM (e.g., Ethereum ↔ Solana), additional providers, provider selection, and multiple quote options. + +## Fees + +Swap fees are integrator fees: a percentage of the swap amount, expressed in basis points, applied to quote amounts and collected onchain during execution. + +- **Your integrator fee**: set via (`feeBps`), along with the payout wallet (`feeReceiverWalletAddress`, a wallet address you control). Fees accrue onchain to that wallet. +- **Cap**: `feeBps` is bounded by a Turnkey-controlled maximum threshold. Requests above the cap are rejected. + +Fees are configured **on the parent organization** and applied to every swap submitted by any sub-organization under it. Sub-organizations cannot override the parent's fee configuration. + + + To change your fee configuration, submit `enable_swap` again with the new values. Quote and execute activities submitted after the change immediately use the new fee configuration. + + +## API surface + +Swap adds three activities: + +| Activity | Endpoint _To Confirm_ | Purpose | +| :-- | :-- | :-- | +| `ACTIVITY_TYPE_ENABLE_SWAP` | `POST /public/v1/submit/enable_swap` | Enable swaps on your parent organization and set your fee | +| `ACTIVITY_TYPE_GET_SWAP_QUOTE` | `POST /public/v1/submit/get_swap_quote` | Indicative fee-aware quote for an input token / amount pair | +| `ACTIVITY_TYPE_EXECUTE_SWAP` | `POST /public/v1/submit/execute_swap` | Sign, broadcast, and settle a swap from a sub-org wallet | + +and one query for status: + +| Query | Endpoint _To Confirm_ | Purpose | +| :-- | :-- | :-- | +| Swap status | `POST /public/v1/query/get_swap_status` | Poll the full swap lifecycle. Handles same-chain and cross-chain with a unified response shape. | + + + Swap requests are stamped and submitted like any other Turnkey request. See [Stamps](/api-reference/overview/stamps) and [Submissions](/api-reference/activities/overview). There are no Swap-specific SDK methods during the early access period, so the examples on these pages use cURL and the generic `request` method of [`@turnkey/http`](https://www.npmjs.com/package/@turnkey/http)'s `TurnkeyClient`. + + +## Trust boundary + +Swap intentionally does not expose executable provider data to API callers. The user-signed `ExecuteSwapIntent` is the source of truth for `inputToken`, `outputToken`, `inputAmount`, `walletAccount`, `sponsor`, and `slippage`. Fee configuration, provider quote payloads, transaction calldata, Permit2 signatures, and approval payloads are all handled server-side and never accepted from callers. + +Policy engines evaluate the derived on-chain transaction, not the swap intent — so existing wallet policies apply to the resulting transfer behavior. + +## Explore + + + + One-time parent-org setup with fee configuration. + + + + Indicative fee-aware quote for display. + + + + One activity, optionally gas-sponsored. + + + + Same-chain and cross-chain lifecycle. + + + + Enable, quote, execute, and confirm. + + \ No newline at end of file diff --git a/welcome.mdx b/welcome.mdx index fbe6e776..af8d8010 100644 --- a/welcome.mdx +++ b/welcome.mdx @@ -5,455 +5,458 @@ mode: "custom" ---
+ {/* ── HERO ─────────────────────────────────────────────────────────────── */} -{/* ── HERO ─────────────────────────────────────────────────────────────── */} +
+
+ -
-
- - -
-
-

- Secure, flexible, -
- and scalable wallet infrastructure -

-

- Turnkey is infrastructure for generating wallets and keys, signing transactions, and - controlling who can use them, when, and how. Private keys are secured in hardware-isolated - enclaves and never exposed — not even to Turnkey. -

-
{ - if (typeof window !== "undefined") { - document.dispatchEvent( - new KeyboardEvent("keydown", { key: "k", metaKey: true, bubbles: true }), - ); - } - }} - > - - - - Search... - ⌘K +
-
-
- Turnkey hero illustration - Turnkey hero illustration -
-
-
+
+

+ Secure, flexible, -{/* ── BUILD WITH TURNKEY ───────────────────────────────────────────────── */} +
-
+ and scalable wallet infrastructure +

-
-
-

Build with Turnkey

-

- Build at whichever level best meets your needs. Start with Solutions for common patterns, go - deeper with our SDKs for more control, or reach all the way down to the API for complete - flexibility. The full stack is always available. -

-
- - -
-
-
- Solutions layer - Solutions layer - SDK layer - SDK layer - API layer - API layer + + + + + Search... + ⌘K +
-
-
- -
+
+
+
+ Solutions layer -
+ Solutions layer -{/* ── SOLUTIONS ────────────────────────────────────────────────────────── */} + SDK layer -
-

Explore Turnkey Solutions

+ SDK layer -
+ API layer -
-
- Embedded Wallets -
-
- - Embedded Wallets - -

- Build wallets directly into your app. Users authenticate with email, passkeys, or social login. - You control the experience. -

-
- -
+ API layer +
+
-
-
- Company Wallets -
-
- - Company Wallets - -

- Automate your onchain operations with wallets, keys and programmable controls purpose built for - scale. -

-
- -
+
+ + +
-
+
+ { const el = e.currentTarget.closest('.tk-build'); el?.setAttribute('data-selected', 'a'); el?.setAttribute('data-user-picked', 'true'); }} onFocus={(e) => { const el = e.currentTarget.closest('.tk-build'); el?.setAttribute('data-selected', 'a'); el?.setAttribute('data-user-picked', 'true'); }} onTouchStart={(e) => { const el = e.currentTarget.closest('.tk-build'); el?.setAttribute('data-selected', 'a'); el?.setAttribute('data-user-picked', 'true'); }}> + -
+ Solutions + Pre-built patterns for common use cases -{/* ── FEATURES ─────────────────────────────────────────────────────────── */} + +
-
-

Explore our features

+ { const el = e.currentTarget.closest('.tk-build'); el?.setAttribute('data-selected', 'b'); el?.setAttribute('data-user-picked', 'true'); }} onFocus={(e) => { const el = e.currentTarget.closest('.tk-build'); el?.setAttribute('data-selected', 'b'); el?.setAttribute('data-user-picked', 'true'); }} onTouchStart={(e) => { const el = e.currentTarget.closest('.tk-build'); el?.setAttribute('data-selected', 'b'); el?.setAttribute('data-user-picked', 'true'); }}> + - +
-
-

Control access

-
-
- - - - - - - Sessions - -
- - - - - - - Delegated access - -
- - - - - - - Policy engine - +
+ + {/* ── SOLUTIONS ────────────────────────────────────────────────────────── */} + +
+

Explore Turnkey Solutions

+ +
+
+
+ Embedded Wallets +
+ +
+ + Embedded Wallets + + +

+ Build wallets directly into your app. Users authenticate with email, passkeys, or social login. + You control the experience. +

+
+ +
-
-
-

Transact programmatically

-
-
- - - - - - - Transaction management - -
- - - - Gas Sponsorship - -
- - - - - - - Balances - -
- - - - - - - Fiat onramp - +
+
+ Company Wallets +
+ +
+ + Company Wallets + + +

+ Automate your onchain operations with wallets, keys and programmable controls purpose built for + scale. +

+
+ +
-
+
+
+ Key Management +
+ +
+ + Key Management + + +

+ Enterprise-grade security for your most sensitive keys — hardware-backed with programmable + access controls. +

+
+ + +
+
-
- -{/* ── WHITEPAPER CTA ───────────────────────────────────────────────────── */} - -
- -
-
-
-
- Turnkey Whitepaper + +
+ + {/* ── FEATURES ─────────────────────────────────────────────────────────── */} + +
+

Explore our features

+ +
+ -
-

- Read our whitepaper for an in-depth look at Turnkey's security model and verifiable key - management infrastructure. -

- - - Read the Turnkey Whitepaper - + + + +
-
-
+ {/* ── WHITEPAPER CTA ───────────────────────────────────────────────────── */} + +
+ +
+
+
+
+ Turnkey Whitepaper +
+ +
+

+ Read our whitepaper for an in-depth look at Turnkey's security model and verifiable key + management infrastructure. +

+ + + + + Read the Turnkey Whitepaper + +
+
+
+
+
\ No newline at end of file From 004559e63fc581cab54ca0192201028234624f39 Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 22:17:29 +0000 Subject: [PATCH 2/6] Updated mintlify pages - Updated welcome.mdx - Moved features/transaction-management/swap/enable-swap.mdx - Moved features/transaction-management/swap/track-swap-status.mdx - Moved features/transaction-management/swap/get-swap-quote.mdx - Moved features/transaction-management/swap/execute-swap.mdx - Moved features/transaction-management/swap.mdx - Updated docs.json Mintlify-Source: dashboard-editor --- docs.json | 12 ++++++------ .../transaction-management/swap.mdx | 4 ---- .../transaction-management/swap/enable-swap.mdx | 2 -- .../transaction-management/swap/execute-swap.mdx | 8 -------- .../transaction-management/swap/get-swap-quote.mdx | 2 -- .../swap/track-swap-status.mdx | 2 -- 6 files changed, 6 insertions(+), 24 deletions(-) rename untitled-page.mdx => features/transaction-management/swap.mdx (97%) rename untitled-page-2.mdx => features/transaction-management/swap/enable-swap.mdx (98%) rename untitled-page-3.mdx => features/transaction-management/swap/execute-swap.mdx (98%) rename untitled-page-4.mdx => features/transaction-management/swap/get-swap-quote.mdx (99%) rename untitled-page-5.mdx => features/transaction-management/swap/track-swap-status.mdx (99%) diff --git a/docs.json b/docs.json index b55536a8..55d015bd 100644 --- a/docs.json +++ b/docs.json @@ -210,7 +210,6 @@ "solutions/company-wallets/integration-guide/overview", "solutions/company-wallets/integration-guide/javascript-server", "solutions/company-wallets/integration-guide/golang", - "solutions/company-wallets/integration-guide/ruby", "solutions/company-wallets/integration-guide/rust", "solutions/company-wallets/integration-guide/python" ] @@ -448,11 +447,11 @@ "group": "Swaps", "tag": "Early Access", "pages": [ - "untitled-page", - "untitled-page-2", - "untitled-page-4", - "untitled-page-3", - "untitled-page-5" + "features/transaction-management/swap", + "features/transaction-management/swap/enable-swap", + "features/transaction-management/swap/get-swap-quote", + "features/transaction-management/swap/execute-swap", + "features/transaction-management/swap/track-swap-status" ] } ] @@ -920,6 +919,7 @@ "solutions/company-wallets/integration-guide/javascript-server", "solutions/company-wallets/integration-guide/golang", "solutions/company-wallets/integration-guide/ruby", + "solutions/company-wallets/integration-guide/ruby", "solutions/company-wallets/integration-guide/rust", "solutions/company-wallets/integration-guide/python", { diff --git a/untitled-page.mdx b/features/transaction-management/swap.mdx similarity index 97% rename from untitled-page.mdx rename to features/transaction-management/swap.mdx index 9ee356c4..496ee876 100644 --- a/untitled-page.mdx +++ b/features/transaction-management/swap.mdx @@ -94,8 +94,4 @@ Policy engines evaluate the derived on-chain transaction, not the swap intent Same-chain and cross-chain lifecycle. - - - Enable, quote, execute, and confirm. - \ No newline at end of file diff --git a/untitled-page-2.mdx b/features/transaction-management/swap/enable-swap.mdx similarity index 98% rename from untitled-page-2.mdx rename to features/transaction-management/swap/enable-swap.mdx index 97dc7e9e..606decda 100644 --- a/untitled-page-2.mdx +++ b/features/transaction-management/swap/enable-swap.mdx @@ -5,8 +5,6 @@ mode: "wide" noindex: true --- -import SwapBetaNote from "/snippets/shared/swaps-beta-note.mdx"; - `ACTIVITY_TYPE_ENABLE_SWAP` is a parent-organization configuration activity that writes `ORG_FEATURE_SWAP_CONFIG` — the source of truth for whether swaps are enabled, your integrator fee, and the wallet that receives fees. Enable must complete before any sub-organization can call `get_swap_quote` or `execute_swap`. ## Prerequisites diff --git a/untitled-page-3.mdx b/features/transaction-management/swap/execute-swap.mdx similarity index 98% rename from untitled-page-3.mdx rename to features/transaction-management/swap/execute-swap.mdx index c428693b..6868366f 100644 --- a/untitled-page-3.mdx +++ b/features/transaction-management/swap/execute-swap.mdx @@ -5,12 +5,8 @@ mode: "wide" noindex: true --- -import SwapBetaNote from "/snippets/shared/swaps-beta-note.mdx"; - `ACTIVITY_TYPE_EXECUTE_SWAP` is the signing activity that runs a swap end-to-end. The caller signs a swap intent. Turnkey fetches a fresh executable quote from the provider, constructs the transaction (batching any required ERC-20 approvals via Permit2), signs it with the wallet in the intent, and broadcasts. Policy engines evaluate the derived transaction, so existing wallet policies apply to the resulting on-chain transfer. - - ## Prerequisites - The parent organization has [enabled swaps](/features/transaction-management/swap/enable-swap). Execute requests against orgs with swaps disabled are rejected. @@ -55,8 +51,6 @@ import SwapBetaNote from "/snippets/shared/swaps-beta-note.mdx"; Whether to sponsor the transaction's gas via Gas Station. Defaults to `false`. Requires a Pro plan or higher when set to `true`. _(To Confirm: plan gating.)_ - - ```bash title="cURL" curl --request POST \ --url https://api.turnkey.com/public/v1/submit/execute_swap \ @@ -105,8 +99,6 @@ const { activity } = await client.request("/public/v1/submit/execute_swap", { }); ``` - - The activity result returns a poll handle: ```json diff --git a/untitled-page-4.mdx b/features/transaction-management/swap/get-swap-quote.mdx similarity index 99% rename from untitled-page-4.mdx rename to features/transaction-management/swap/get-swap-quote.mdx index b669e255..fb646d15 100644 --- a/untitled-page-4.mdx +++ b/features/transaction-management/swap/get-swap-quote.mdx @@ -5,8 +5,6 @@ mode: "wide" noindex: true --- -## Prerequisites - - The parent organization has [enabled swaps](/features/transaction-management/swap/enable-swap) with a valid `feeReceiverWalletAddress` and `feeBps`. Quote requests against orgs with swaps disabled are rejected. - The token pair maps to a supported provider route (0x in V1). Same-chain and EVM ↔ EVM cross-chain routes are supported. diff --git a/untitled-page-5.mdx b/features/transaction-management/swap/track-swap-status.mdx similarity index 99% rename from untitled-page-5.mdx rename to features/transaction-management/swap/track-swap-status.mdx index 71c3975c..2ba0e600 100644 --- a/untitled-page-5.mdx +++ b/features/transaction-management/swap/track-swap-status.mdx @@ -5,8 +5,6 @@ mode: "wide" noindex: true --- -import SwapBetaNote from "/snippets/shared/swaps-beta-note.mdx"; - `execute_swap` returns a poll handle. Query `get_swap_status` with that handle to observe the full lifecycle. Same-chain and cross-chain swaps share the same endpoint and the same response shape. The endpoint normalizes provider-specific state machines to three states: `PENDING`, `COMPLETED`, `FAILED`. ## Lifecycle model From b759692b20e836d73b9631fa296bed72b17a7b46 Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 22:23:28 +0000 Subject: [PATCH 3/6] Updated mintlify pages - Updated welcome.mdx - Updated features/transaction-management/swap/enable-swap.mdx - Updated features/transaction-management/swap/track-swap-status.mdx - Updated features/transaction-management/swap/get-swap-quote.mdx - Updated features/transaction-management/swap/execute-swap.mdx - Updated features/transaction-management/swap.mdx - Updated snippets/shared/balance-concepts-2.mdx Mintlify-Source: dashboard-editor --- features/transaction-management/swap.mdx | 2 +- snippets/shared/balance-concepts-2.mdx | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/features/transaction-management/swap.mdx b/features/transaction-management/swap.mdx index 496ee876..87facd94 100644 --- a/features/transaction-management/swap.mdx +++ b/features/transaction-management/swap.mdx @@ -7,7 +7,7 @@ noindex: true import SwapBetaNote from "/snippets/shared/swaps-beta-note.mdx"; -Swap lets wallets in your organization exchange tokens through DEX aggregators via a single Turnkey activity, with the usual audit trail and policy controls. Same-chain and EVM ↔ EVM cross-chain routes are supported. You can take an integrator fee on every swap, paid on-chain to a wallet you control. +Swaps allow wallets in your organization exchange tokens through DEX aggregators via a single Turnkey activity, with the usual audit trail and policy controls. Same-chain and EVM ↔ EVM cross-chain routes are supported. You can take an integrator fee on every swap, paid on-chain to a wallet you control. diff --git a/snippets/shared/balance-concepts-2.mdx b/snippets/shared/balance-concepts-2.mdx index c82c338a..83512bbb 100644 --- a/snippets/shared/balance-concepts-2.mdx +++ b/snippets/shared/balance-concepts-2.mdx @@ -1,7 +1,3 @@ ---- -title: "swaps-beta-note.mdx" ---- - Swaps are in early access. [Contact us](https://www.turnkey.com/contact-us) to enable it for your organization. \ No newline at end of file From 5757a167314189baa5553041220e5cc35ed7a07c Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 19:30:23 +0000 Subject: [PATCH 4/6] Updated mintlify pages - Updated features/transaction-management/swap.mdx - Created snippets/shared/swaps-beta-note.mdx - Deleted snippets/shared/balance-concepts-2.mdx Mintlify-Source: dashboard-editor --- features/transaction-management/swap.mdx | 4 +--- .../shared/{balance-concepts-2.mdx => swaps-beta-note.mdx} | 4 ++++ 2 files changed, 5 insertions(+), 3 deletions(-) rename snippets/shared/{balance-concepts-2.mdx => swaps-beta-note.mdx} (77%) diff --git a/features/transaction-management/swap.mdx b/features/transaction-management/swap.mdx index 87facd94..4a86dc3e 100644 --- a/features/transaction-management/swap.mdx +++ b/features/transaction-management/swap.mdx @@ -5,12 +5,10 @@ mode: "wide" noindex: true --- -import SwapBetaNote from "/snippets/shared/swaps-beta-note.mdx"; +import SwapsBetaNote from "/snippets/shared/swaps-beta-note.mdx"; Swaps allow wallets in your organization exchange tokens through DEX aggregators via a single Turnkey activity, with the usual audit trail and policy controls. Same-chain and EVM ↔ EVM cross-chain routes are supported. You can take an integrator fee on every swap, paid on-chain to a wallet you control. - - ## What are Swaps Swaps connects Turnkey wallets to DEX aggregators through a unified interface. 0x is supported today; additional providers are on the roadmap. Callers submit signed intents describing what they want to swap while Turnkey handles quote fetching, transaction construction, ERC-20 approvals via Permit2, and broadcast. Callers never see provider quote payloads, transaction calldata, or approval data. diff --git a/snippets/shared/balance-concepts-2.mdx b/snippets/shared/swaps-beta-note.mdx similarity index 77% rename from snippets/shared/balance-concepts-2.mdx rename to snippets/shared/swaps-beta-note.mdx index 83512bbb..c82c338a 100644 --- a/snippets/shared/balance-concepts-2.mdx +++ b/snippets/shared/swaps-beta-note.mdx @@ -1,3 +1,7 @@ +--- +title: "swaps-beta-note.mdx" +--- + Swaps are in early access. [Contact us](https://www.turnkey.com/contact-us) to enable it for your organization. \ No newline at end of file From 25eb8d6c251831fd2a5fba521cf9d70df5622060 Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 20:33:58 +0000 Subject: [PATCH 5/6] Updated mintlify pages - Updated features/transaction-management/swap.mdx - Updated features/transaction-management/swap/enable-swap.mdx - Updated features/transaction-management/swap/get-swap-quote.mdx - Updated features/transaction-management/swap/execute-swap.mdx - Updated features/transaction-management/swap/track-swap-status.mdx Mintlify-Source: dashboard-editor --- features/transaction-management/swap.mdx | 17 +-- .../swap/enable-swap.mdx | 46 ++++---- .../swap/execute-swap.mdx | 26 +++-- .../swap/get-swap-quote.mdx | 100 ++++++++---------- .../swap/track-swap-status.mdx | 23 ++-- 5 files changed, 106 insertions(+), 106 deletions(-) diff --git a/features/transaction-management/swap.mdx b/features/transaction-management/swap.mdx index 4a86dc3e..46a3d639 100644 --- a/features/transaction-management/swap.mdx +++ b/features/transaction-management/swap.mdx @@ -17,7 +17,7 @@ Swaps enablement is a one-time setup step per parent organization. After that, e ## How it works -1. Enable swaps once on your parent organization via [`enable_swap`](/features/transaction-management/swap/enable-swap), setting your integrator fee (bps) and fee-receiver wallet. +1. Enable swaps once on your parent organization via [`upsert_swap_config`](/features/transaction-management/swap/enable-swap), setting your integrator fee (bps) and fee-receiver wallet. 2. Optionally call [`get_swap_quote`](/features/transaction-management/swap/get-swap-quote) for an indicative quote to display to users. Quotes reflect your active fee configuration. 3. Submit [`execute_swap`](/features/transaction-management/swap/execute-swap) from the sub-organization whose wallet is swapping. Turnkey handles quote refresh, Permit2 approvals, and transaction construction internally. For cross-chain routes, the origin-chain transaction is broadcast here. 4. Poll swap status until settlement. Same-chain swaps complete on origin inclusion; cross-chain swaps have a longer lifecycle that continues until the destination-chain leg settles or funds are recovered. See [Track swap status](/features/transaction-management/swap/track-swap-status). @@ -45,23 +45,24 @@ Swap fees are integrator fees: a percentage of the swap amount, expressed in bas Fees are configured **on the parent organization** and applied to every swap submitted by any sub-organization under it. Sub-organizations cannot override the parent's fee configuration. - To change your fee configuration, submit `enable_swap` again with the new values. Quote and execute activities submitted after the change immediately use the new fee configuration. + To change your fee configuration, submit `upsert_swap_config` again with the new values. Quote and execute activities submitted after the change immediately use the new fee configuration. ## API surface -Swap adds three activities: +Swaps add three activities: -| Activity | Endpoint _To Confirm_ | Purpose | +| Activity | Endpoint | Purpose | | :-- | :-- | :-- | -| `ACTIVITY_TYPE_ENABLE_SWAP` | `POST /public/v1/submit/enable_swap` | Enable swaps on your parent organization and set your fee | -| `ACTIVITY_TYPE_GET_SWAP_QUOTE` | `POST /public/v1/submit/get_swap_quote` | Indicative fee-aware quote for an input token / amount pair | +| `ACTIVITY_TYPE_UPSERT_SWAP_CONFIG` | `POST /public/v1/submit/upsert_swap_config` | Enable swaps on your parent organization and set your fee configuration | | `ACTIVITY_TYPE_EXECUTE_SWAP` | `POST /public/v1/submit/execute_swap` | Sign, broadcast, and settle a swap from a sub-org wallet | +| `ACTIVITY_TYPE_CLAIM_SWAP_FEES` | `POST /public/v1/submit/claim_swap_fees` | Claim accrued integrator fees to your fee-receiver wallet | -and one query for status: +and two queries: -| Query | Endpoint _To Confirm_ | Purpose | +| Query | Endpoint | Purpose | | :-- | :-- | :-- | +| Swap quote | `POST /public/v1/query/get_swap_quote` | Indicative fee-aware quote for an input token / amount pair | | Swap status | `POST /public/v1/query/get_swap_status` | Poll the full swap lifecycle. Handles same-chain and cross-chain with a unified response shape. | diff --git a/features/transaction-management/swap/enable-swap.mdx b/features/transaction-management/swap/enable-swap.mdx index 606decda..dd657535 100644 --- a/features/transaction-management/swap/enable-swap.mdx +++ b/features/transaction-management/swap/enable-swap.mdx @@ -5,7 +5,7 @@ mode: "wide" noindex: true --- -`ACTIVITY_TYPE_ENABLE_SWAP` is a parent-organization configuration activity that writes `ORG_FEATURE_SWAP_CONFIG` — the source of truth for whether swaps are enabled, your integrator fee, and the wallet that receives fees. Enable must complete before any sub-organization can call `get_swap_quote` or `execute_swap`. +`ACTIVITY_TYPE_UPSERT_SWAP_CONFIG` is a parent-organization configuration activity that writes `FEATURE_NAME_SWAP_CONFIG` — the source of truth for your integrator fee configuration and the wallet that receives fees. This config must be set before any sub-organization can call `get_swap_quote` or `execute_swap`. ## Prerequisites @@ -13,10 +13,10 @@ noindex: true - You control a wallet that will receive integrator fees. It must be a valid address for the target route and provider _(To Confirm — likely any EVM address in V1)_. - You've decided on your fee rate in basis points. See [Choose your fee configuration](#choose-your-fee-configuration). -## Submit enable\_swap +## Submit upsert\_swap\_config - `ACTIVITY_TYPE_ENABLE_SWAP` + `ACTIVITY_TYPE_UPSERT_SWAP_CONFIG` @@ -27,10 +27,6 @@ noindex: true Unique identifier of the **parent** organization. Requests from sub-organizations are rejected. - - Whether quote and execute activities are permitted. Set `true` to enable, `false` to disable. - - The wallet address that receives integrator fees on-chain. Must be valid for the route and provider _(To Confirm — likely any EVM address in V1)_. @@ -39,18 +35,25 @@ noindex: true Your integrator fee in basis points, expressed as a stringified integer. For example, `"50"` = 0.5%. Must be non-negative and no greater than the Turnkey-defined maximum. + + Optional. A separate integrator fee in basis points applied to stablecoin-to-stablecoin swaps _(To Confirm — verify this is the exact scope)_. + + + + Optional. The DEX aggregator provider. Omit in V1 — `0x` is the only supported provider and is used by default. + + ```bash title="cURL" curl --request POST \ - --url https://api.turnkey.com/public/v1/submit/enable_swap \ + --url https://api.turnkey.com/public/v1/submit/upsert_swap_config \ --header 'Accept: application/json' \ --header 'Content-Type: application/json' \ --header "X-Stamp: (see Stamps)" \ --data '{ - "type": "ACTIVITY_TYPE_ENABLE_SWAP", + "type": "ACTIVITY_TYPE_UPSERT_SWAP_CONFIG", "timestampMs": " (e.g. 1745474677453)", "organizationId": "", "parameters": { - "enabled": true, "feeReceiverWalletAddress": "", "feeBps": "50" } @@ -60,7 +63,6 @@ curl --request POST \ ```javascript title="JavaScript" import { TurnkeyClient } from "@turnkey/http"; import { ApiKeyStamper } from "@turnkey/api-key-stamper"; - const client = new TurnkeyClient( { baseUrl: "https://api.turnkey.com" }, new ApiKeyStamper({ @@ -68,34 +70,30 @@ const client = new TurnkeyClient( apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY, }), ); - -const { activity } = await client.request("/public/v1/submit/enable_swap", { - type: "ACTIVITY_TYPE_ENABLE_SWAP", +const { activity } = await client.request("/public/v1/submit/upsert_swap_config", { + type: "ACTIVITY_TYPE_UPSERT_SWAP_CONFIG", timestampMs: String(Date.now()), organizationId: "", parameters: { - enabled: true, feeReceiverWalletAddress: "", feeBps: "50", }, }); ``` -`enable_swap` completes inline. The activity result echoes the applied configuration: +`upsert_swap_config` completes inline. The activity result echoes the applied configuration: ```json { "activity": { "id": "", "status": "ACTIVITY_STATUS_COMPLETED", - "type": "ACTIVITY_TYPE_ENABLE_SWAP", + "type": "ACTIVITY_TYPE_UPSERT_SWAP_CONFIG", "result": { - "enableSwapResult": { - "config": { - "enabled": true, - "feeReceiverWalletAddress": "", - "feeBps": "50" - } + "upsertSwapConfigResult": { + "feeReceiverWalletAddress": "", + "feeBps": "50", + "stableFeeBps": "10" } } } @@ -115,7 +113,7 @@ Pick a `feeBps` that reflects your intended margin and confirm the receiver wall ## Change your fee configuration -Configuration changes go through the same `ACTIVITY_TYPE_ENABLE_SWAP` activity — submit again with the updated `feeBps`, `feeReceiverWalletAddress`, or `enabled` flag. There is no separate update activity. +Configuration changes go through the same `ACTIVITY_TYPE_UPSERT_SWAP_CONFIG` activity — submit again with the updated `feeBps`, `feeReceiverWalletAddress`, or `stableFeeBps`. There is no separate update activity. Quote and execute activities submitted **after** the change immediately use the new configuration. Because executable provider data is fetched fresh during `execute_swap`, an in-flight quote followed by an execute will settle at the fee configuration active at execute time, not at quote time _(To Confirm)_. diff --git a/features/transaction-management/swap/execute-swap.mdx b/features/transaction-management/swap/execute-swap.mdx index 6868366f..0b9db2b0 100644 --- a/features/transaction-management/swap/execute-swap.mdx +++ b/features/transaction-management/swap/execute-swap.mdx @@ -9,7 +9,7 @@ noindex: true ## Prerequisites -- The parent organization has [enabled swaps](/features/transaction-management/swap/enable-swap). Execute requests against orgs with swaps disabled are rejected. +- The parent organization has [configured swaps](/features/transaction-management/swap/enable-swap). Execute requests against orgs with no swap configuration set are rejected. - The `walletAccount` in the intent holds enough of the input token to cover `inputAmount`. For non-sponsored swaps it also needs the origin chain's native token for gas. - For cross-chain routes, the destination chain is supported by the provider. See [Supported providers, chains, and routes](/features/transaction-management/swap#supported-providers-chains-and-routes). @@ -40,15 +40,23 @@ noindex: true - The wallet account address to sign with and swap from. Part of the user-signed intent. Authentication identifies the caller. It does not determine the signing wallet. + The wallet account address to sign with and swap from. Part of the user-signed intent. Authentication identifies the caller; it does not determine the signing wallet. - - Slippage tolerance in basis points, expressed as a stringified integer. Enforced at execute time as protection against market movement between quote fetch and settlement. + + The minimum output amount to accept, in raw on-chain units. This is the hard floor enforced at settlement — if the swap would return less, it fails. Typically set to the `minOutputAmount` returned by [`get_swap_quote`](/features/transaction-management/swap/get-swap-quote). + + + + Optional. Slippage tolerance in basis points, expressed as a stringified integer. Used when fetching the executable quote. `minOutputAmount` is the enforced floor at settlement _(To Confirm — exact interaction between `slippage` and `minOutputAmount`)_. - Whether to sponsor the transaction's gas via Gas Station. Defaults to `false`. Requires a Pro plan or higher when set to `true`. _(To Confirm: plan gating.)_ + Whether to sponsor the transaction's gas via Gas Station. Defaults to `false`. Requires a Pro plan or higher when set to `true` _(To Confirm: plan gating)_. + + + + Optional. The DEX aggregator provider. Omit in V1 — `0x` is the only supported provider and is used by default. ```bash title="cURL" @@ -66,6 +74,7 @@ curl --request POST \ "outputToken": "eip155:8453/erc20:0x4200000000000000000000000000000000000006", "inputAmount": "1000000", "walletAccount": "", + "minOutputAmount": "", "slippage": "50", "sponsor": false } @@ -93,6 +102,7 @@ const { activity } = await client.request("/public/v1/submit/execute_swap", { outputToken: "eip155:8453/erc20:0x4200000000000000000000000000000000000006", inputAmount: "1000000", walletAccount: "", + minOutputAmount: "", slippage: "50", sponsor: false, }, @@ -109,14 +119,16 @@ The activity result returns a poll handle: "type": "ACTIVITY_TYPE_EXECUTE_SWAP", "result": { "executeSwapResult": { - "sendTransactionStatusId": "" + "sendTransactionStatusId": "", + "provider": "0x", + "quoteId": "" } } } } ``` -_(Field name `sendTransactionStatusId` `To Confirm`. Response shape mirrors other send-transaction activities.)_ +`ExecuteSwapResult` returns `sendTransactionStatusId` (used to poll status), plus optional `provider` and `quoteId`. ## Gas: sponsored vs. self-funded diff --git a/features/transaction-management/swap/get-swap-quote.mdx b/features/transaction-management/swap/get-swap-quote.mdx index fb646d15..822fd116 100644 --- a/features/transaction-management/swap/get-swap-quote.mdx +++ b/features/transaction-management/swap/get-swap-quote.mdx @@ -5,55 +5,49 @@ mode: "wide" noindex: true --- -- The parent organization has [enabled swaps](/features/transaction-management/swap/enable-swap) with a valid `feeReceiverWalletAddress` and `feeBps`. Quote requests against orgs with swaps disabled are rejected. -- The token pair maps to a supported provider route (0x in V1). Same-chain and EVM ↔ EVM cross-chain routes are supported. +- The parent organization has [configured swaps](/features/transaction-management/swap/enable-swap) with a valid `feeReceiverWalletAddress` and `feeBps`. Quote requests against orgs with no swap configuration set are rejected. -## Submit get\_swap\_quote +## Call get\_swap\_quote - - `ACTIVITY_TYPE_GET_SWAP_QUOTE` - - - - Timestamp (in milliseconds) of the request, used to verify liveness. - +`get_swap_quote` is a **query**, not an activity — it returns synchronously and is not stamped into an activity envelope. Send the request fields at the top level (there is no `parameters` object, `type`, or `timestampMs`). - Unique identifier of the sub-organization requesting the quote. The parent organization's active `ORG_FEATURE_SWAP_CONFIG` is applied server-side. + Unique identifier of the sub-organization requesting the quote. The parent organization's active `FEATURE_NAME_SWAP_CONFIG` is applied server-side. - + CAIP-19 identifier for the token being sold. Example: `eip155:8453/erc20:0x833589fCD6EDB6E08f4c7C32D4f71b54bdA02913` (USDC on Base). The origin chain is derived from this identifier. - + CAIP-19 identifier for the token being bought. Example: `eip155:8453/erc20:0x4200000000000000000000000000000000000006` (WETH on Base). Same-chain if the CAIP-2 prefix matches `inputToken`. Otherwise a cross-chain route. - + Amount of the input token, in raw on-chain units. For example, `"1000000"` for 1 USDC at 6 decimals. - - Slippage tolerance in basis points, expressed as a stringified integer. For example, `"50"` = 0.5%. Applied to `minOutputAmount` computation. _(To Confirm: required vs. optional, and whether a server default applies if omitted.)_ + + The address of the wallet account that will perform the swap. Used to compute an accurate quote (balances and allowances) _(To Confirm — exact format: on-chain address vs. wallet-account identifier)_. + + + + Optional. Slippage tolerance in basis points, expressed as a stringified integer. For example, `"50"` = 0.5%. Applied to `minOutputAmount` computation _(To Confirm — server default when omitted)_. ```bash title="cURL" curl --request POST \ - --url https://api.turnkey.com/public/v1/submit/get_swap_quote \ + --url https://api.turnkey.com/public/v1/query/get_swap_quote \ --header 'Accept: application/json' \ --header 'Content-Type: application/json' \ --header "X-Stamp: (see Stamps)" \ --data '{ - "type": "ACTIVITY_TYPE_GET_SWAP_QUOTE", - "timestampMs": " (e.g. 1745474677453)", "organizationId": "", - "parameters": { - "inputToken": "eip155:8453/erc20:0x833589fCD6EDB6E08f4c7C32D4f71b54bdA02913", - "outputToken": "eip155:8453/erc20:0x4200000000000000000000000000000000000006", - "inputAmount": "1000000", - "slippage": "50" - } + "inputToken": "eip155:8453/erc20:0x833589fCD6EDB6E08f4c7C32D4f71b54bdA02913", + "outputToken": "eip155:8453/erc20:0x4200000000000000000000000000000000000006", + "inputAmount": "1000000", + "walletAccount": "", + "slippage": "50" }' ``` @@ -69,47 +63,45 @@ const client = new TurnkeyClient( }), ); -const { activity } = await client.request("/public/v1/submit/get_swap_quote", { - type: "ACTIVITY_TYPE_GET_SWAP_QUOTE", - timestampMs: String(Date.now()), +// Queries return the response body directly — there is no `activity` wrapper. +const { quotes } = await client.request("/public/v1/query/get_swap_quote", { organizationId: "", - parameters: { - inputToken: "eip155:8453/erc20:0x833589fCD6EDB6E08f4c7C32D4f71b54bdA02913", - outputToken: "eip155:8453/erc20:0x4200000000000000000000000000000000000006", - inputAmount: "1000000", - slippage: "50", - }, + inputToken: "eip155:8453/erc20:0x833589fCD6EDB6E08f4c7C32D4f71b54bdA02913", + outputToken: "eip155:8453/erc20:0x4200000000000000000000000000000000000006", + inputAmount: "1000000", + walletAccount: "", + slippage: "50", }); + +const bestQuote = quotes[0]; ``` Response: ```json { - "activity": { - "id": "", - "status": "ACTIVITY_STATUS_COMPLETED", - "type": "ACTIVITY_TYPE_GET_SWAP_QUOTE", - "result": { - "getSwapQuoteResult": { - "quoteId": "", - "inputToken": "eip155:8453/erc20:0x833589fCD6EDB6E08f4c7C32D4f71b54bdA02913", - "outputToken": "eip155:8453/erc20:0x4200000000000000000000000000000000000006", - "inputAmount": "1000000", - "outputAmount": "", - "minOutputAmount": "" - } + "inputToken": "eip155:8453/erc20:0x833589fCD6EDB6E08f4c7C32D4f71b54bdA02913", + "outputToken": "eip155:8453/erc20:0x4200000000000000000000000000000000000006", + "inputAmount": "1000000", + "quotes": [ + { + "quoteId": "", + "provider": "0x", + "outputAmount": "", + "minOutputAmount": "" } - } + ] } ``` -_(Response shape `To Confirm`. Field name `getSwapQuoteResult` follows Turnkey activity conventions.)_ - ### Response fields -- `quoteId`: opaque identifier for the quote. Not required for execution. Useful for logging and correlation. -- `inputToken`, `outputToken`, `inputAmount`: echoed from the request for confirmation. +The top-level `inputToken`, `outputToken`, and `inputAmount` are echoed from the request. Quotes are returned in a `quotes` array — in V1 this contains a single option; multiple options are on the roadmap. + +Each entry in `quotes` contains: + +- `quoteId`: opaque identifier for the quote. Informational only — `execute_swap` does not accept a `quoteId`, so it cannot be replayed. Useful for logging and correlation. +- `provider`: the DEX aggregator that produced this quote (`0x` in V1). - `outputAmount`: expected output amount, fee-adjusted, in raw on-chain units. - `minOutputAmount`: minimum output the caller should accept at execute time, given the requested slippage. Derived from `outputAmount` and the caller's slippage tolerance. @@ -124,9 +116,9 @@ If you display quote amounts to end users, refresh the quote near the moment of ## How fees are applied -The `outputAmount` returned in a quote already reflects your organization's `feeBps` from `ORG_FEATURE_SWAP_CONFIG`. There is no separate fee amount to add or subtract on the client. The end user's displayed receive amount is the quote's `outputAmount` (or the actual settled amount at execute time). +Each quote's `outputAmount` already reflects your organization's `feeBps` from `FEATURE_NAME_SWAP_CONFIG`. There is no separate fee amount to add or subtract on the client. The end user's displayed receive amount is a quote's `outputAmount` (or the actual settled amount at execute time). -Fee configuration is snapshotted server-side per activity. If your fee configuration changes between quote and execute, the execute settles at the fee configuration active when it runs, not when the quote was fetched. _(To Confirm.)_ +Fee configuration is snapshotted server-side per activity. If your fee configuration changes between quote and execute, the execute settles at the fee configuration active when it runs, not when the quote was fetched. ## Same-chain vs. cross-chain quotes diff --git a/features/transaction-management/swap/track-swap-status.mdx b/features/transaction-management/swap/track-swap-status.mdx index 2ba0e600..b65ac573 100644 --- a/features/transaction-management/swap/track-swap-status.mdx +++ b/features/transaction-management/swap/track-swap-status.mdx @@ -51,37 +51,34 @@ Response: ```json { "status": "PENDING | COMPLETED | FAILED", - "swapKind": "same_chain | cross_chain", - "provider": "zero_x", + "swapKind": "SAME_CHAIN | CROSS_CHAIN", + "provider": "", "inputToken": "", "outputToken": "", "inputAmount": "", - "quotedOutputAmount": "", - "minOutputAmount": "", - "outputAmount": "", + "originTxHash": "", "destinationTxHash": "", + "outputAmount": "", "settledAsset": "", "settledAmount": "", "settlementTxHash": "", "providerStatus": "", - "updatedAt": "" + "updatedAt": "" } ``` -_(Field names and JSON casing `To Confirm`.)_ - ### Response fields - `status`: normalized state. `PENDING`, `COMPLETED`, or `FAILED`. -- `swapKind`: `same_chain` or `cross_chain`. Set at execute time; does not change. -- `provider`: the provider that executed the swap. Currently `zero_x` in V1. +- `swapKind`: `SAME_CHAIN` or `CROSS_CHAIN`. Set at execute time; does not change. +- `provider`: the provider that executed the swap. Present when known. - `inputToken`, `outputToken`, `inputAmount`: echoed from the execute intent. -- `quotedOutputAmount`, `minOutputAmount`: from the quote. Always present. `minOutputAmount` is a guaranteed floor. -- `outputAmount`: actual amount received, on `COMPLETED`. May lag the `status` transition by seconds while the monitor fetches the final settled amount. +- `originTxHash`: origin-chain transaction hash. Present once the swap is broadcast, for both same-chain and cross-chain routes. - `destinationTxHash`: destination-chain transaction on `COMPLETED`. Cross-chain only. +- `outputAmount`: actual amount received, on `COMPLETED`. May lag the `status` transition by seconds while the monitor fetches the final settled amount. - `settledAsset`, `settledAmount`, `settlementTxHash`: on `FAILED`. See [What FAILED means](#what-failed-means). - `providerStatus`: raw provider status passthrough for UI use. Cross-chain only. Not normative. -- `updatedAt`: last observed state change. +- `updatedAt`: last observed state change, as a Unix timestamp in milliseconds (stringified). ## Same-chain vs. cross-chain differences From b0d7e1941e0c5b939a94fb3a69ab0a543738ffa5 Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 18:58:24 +0000 Subject: [PATCH 6/6] Updated mintlify pages - Updated features/transaction-management/swap.mdx Mintlify-Source: dashboard-editor --- features/transaction-management/swap.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/features/transaction-management/swap.mdx b/features/transaction-management/swap.mdx index 46a3d639..27af3541 100644 --- a/features/transaction-management/swap.mdx +++ b/features/transaction-management/swap.mdx @@ -5,7 +5,9 @@ mode: "wide" noindex: true --- -import SwapsBetaNote from "/snippets/shared/swaps-beta-note.mdx"; + + Swaps are in early access. [Contact us](https://www.turnkey.com/contact-us) to enable it for your organization. + Swaps allow wallets in your organization exchange tokens through DEX aggregators via a single Turnkey activity, with the usual audit trail and policy controls. Same-chain and EVM ↔ EVM cross-chain routes are supported. You can take an integrator fee on every swap, paid on-chain to a wallet you control.