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"
---
- 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.
-
- 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.
-
+ 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.
+
\ 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.