> For the complete documentation index, see [llms.txt](https://docs.swapkit.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.swapkit.dev/guides/privacy-enabled-swaps.md).

# Privacy Enabled Swaps

Request a swap whose trade details aren't exposed publicly while it executes. Set usePrivacyMode on the quote, then execute the routeId as usual.

A normal cross-chain swap is public end to end: anyone watching the provider's explorer can see what was sold, what was bought, and where the proceeds went. A **privacy enabled swap** keeps those trade details out of public view while the swap executes.

Today privacy mode is fulfilled through **NEAR Intents' Confidential Intents**, so a private route is always a `NEAR` route. The parameter describes the capability rather than any one provider, so further providers can be added behind it without changing your integration.

{% hint style="info" %}
Nothing changes unless you ask for it. A request without `usePrivacyMode`, or with it set to `false`, is quoted and executed exactly as before.
{% endhint %}

### How it works

Privacy is decided once, at quote time, and carried through to execution:

1. [`/v3/quote`](/api-reference/v3-quote-request-a-swap-quote.md) with `usePrivacyMode: true` returns only routes that are private end to end, and asks those providers for their private variant.
2. The quote's `routeId` remembers the choice.
3. `/v3/swap` re-prices that `routeId` with privacy still on, and returns the deposit details for the user to fund.

Your wallet signs and sends the deposit the same way it does for any `NEAR` route. There is no extra signing step and no different transaction format.

### Requesting a private quote

Add `usePrivacyMode: true` to an otherwise ordinary `/v3/quote` request:

{% tabs %}
{% tab title="cURL" %}

```sh
curl -X 'POST' \
  'https://api.swapkit.dev/v3/quote' \
  -H 'Content-Type: application/json' \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "sellAsset": "ETH.ETH",
    "buyAsset": "BTC.BTC",
    "sellAmount": "0.5",
    "sourceAddress": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
    "destinationAddress": "357a3So9CbsNfBBgFYACGvxxS6tMaDoa1P",
    "usePrivacyMode": true
}'
```

{% endtab %}
{% endtabs %}

<table><thead><tr><th width="189">Parameter</th><th width="92">Type</th><th width="89">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>usePrivacyMode</code></td><td><code>boolean</code></td><td>No</td><td>Set to <code>true</code> to only receive routes on which every provider supports a privacy mode. Default: <code>false</code>.</td></tr></tbody></table>

Every other quote parameter behaves as usual. The response has the normal [`/v3/quote` shape](/api-reference/v3-quote-request-a-swap-quote.md), with every route's `providers` limited to privacy-capable ones — today, `["NEAR"]`.

#### It's a filter, not a preference

**Every** provider on a route must support a privacy mode, not just the first leg. A route whose second hop is public exposes the trade just as thoroughly as a fully public one, so partial privacy is treated as no privacy and the route is dropped.

In practice this means:

* **Only single-provider `NEAR` routes are returned today.** Multi-provider routes that pass through a public DEX or bridge are removed, even when they'd price better.
* **Pair coverage narrows to what NEAR Intents supports.** A pair that quotes fine publicly may have no private route at all.
* **The best private price may be worse than the best public one.** The filter runs before routes are ranked, so you're comparing private routes with each other, not against the public market.
* **`providers` still applies.** If you also pass a `providers` list, a route must satisfy both — passing one without `NEAR` leaves nothing to return.

#### When there's no private route

Because privacy filters rather than falls back, `usePrivacyMode: true` can leave you with no routes on a pair that quotes fine without it. You'll see one of:

* `404 noRoutesFound`, or
* `200` with an empty `routes` array and a populated `providerErrors`.

{% hint style="warning" %}
**Never fall back to a public route silently.** The user asked for privacy and wouldn't know they hadn't got it. Decide up front what your product does here: tell the user the pair isn't available privately and let them choose to continue publicly, or don't offer the pair in privacy mode at all. If they choose to continue, re-quote without `usePrivacyMode` — don't reuse a `routeId` from the private request.
{% endhint %}

#### The flag isn't echoed back

The response doesn't tell you a route was quoted privately. If your UI needs to show it — a badge on the confirmation screen, a field in your order history — record it on your side against the `routeId` when you make the request.

#### `/quote` (v2)

The legacy [`/quote`](/legacy-api-v2/quote-v2-request-a-trade-quote.md) endpoint accepts the same `usePrivacyMode` field with the same filtering behaviour. New integrations should use `/v3/quote`; see [API Migration to v3](/guides/api-migration-to-v3.md).

### Executing a private swap

Call [`/v3/swap`](/api-reference/v3-swap-obtain-swap-transaction-details.md) with the `routeId` from the private quote, exactly as you would for any other route:

{% tabs %}
{% tab title="cURL" %}

```sh
curl -X 'POST' \
  'https://api.swapkit.dev/v3/swap' \
  -H 'Content-Type: application/json' \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "routeId": "85bb4fa2-3a65-4d22-b20b-e43d028d83e6",
    "sourceAddress": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
    "destinationAddress": "357a3So9CbsNfBBgFYACGvxxS6tMaDoa1P"
}'
```

{% endtab %}
{% endtabs %}

#### Privacy is inherited from the quote

`/v3/swap` re-prices the route on every call, and that fresh quote is requested with the same privacy mode the original quote used. You **can't** set or change privacy here:

* There's no `usePrivacyMode` parameter on `/v3/swap`. Sending one has no effect — it's ignored rather than rejected.
* A public `routeId` can't be made private at execution, and a private one can't be made public. To change mode, request a new quote.
* `routeId`s are cached for 5 minutes, as usual. After that the `routeId` isn't recognised; request a new quote with `usePrivacyMode: true`.

#### What you send

A private swap settles through a `NEAR` **deposit channel**: the user sends the sell asset to a one-time deposit address, and the swap runs from there. The response is the normal `NEAR` `/v3/swap` response:

* **`tx`** — the transaction to sign and broadcast, as for any `NEAR` route. Its format depends on the sell chain; see [Transaction Formats by txType](/api-reference/transaction-formats-by-txtype.md). With `disableBuildTx: true`, send the sell asset to the deposit address yourself.
* **`meta.providerDepositChannelId`** — the channel's id. Store it: it lets you follow the swap with [`/track`](/api-reference/track-request-the-status-of-a-swap.md) without a transaction hash.
* **`meta.depositChannelExpiration`** — unix seconds after which the channel stops accepting a deposit. Drive any deposit countdown from this, never from `expiration`. See [Deposit-channel fields in `meta`](/api-reference/v3-swap-obtain-swap-transaction-details.md#deposit-channel-fields-in-meta).
* **`memo`** — on sell chains that need one, the same as on a public `NEAR` route.

{% hint style="info" %}
**The deposit itself is still an on-chain transaction.** What privacy mode hides is the trade — what the deposit was swapped into and where the proceeds went. The sending address, the sell asset and the amount are visible on the source chain, as they are for any transfer.
{% endhint %}

Screening, balance checks, slippage settings, `refundAddress` and every other `/v3/swap` behaviour are unchanged — privacy mode doesn't bypass any of them.

### Tracking a private swap

Track a private swap with [`/track`](/api-reference/track-request-the-status-of-a-swap.md) the same way as any other: by `depositChannelId`, `depositAddress`, or `hash` and `chainId`. The difference is what you get back.

**By default, you only see the inbound leg.** The deposit is already public on the source chain, so `/track` shows it. Everything after it is hidden:

* `legs` is the deposit leg, then a placeholder `swap` leg with a blank hash and amounts. Any legs after the swap, such as the payout, are dropped, so even the number of steps is hidden. Before the deposit has been seen there is no deposit leg to show, and `legs` is the placeholder on its own.
* The destination asset, amount and address are `""`.
* The provider and fees are omitted.

`status` and `trackingStatus` are still reported, so you can poll a redacted swap until it completes. You just can't see what it paid out. These responses carry `meta.redacted: true` and a `meta.redactionReason` of `notOwner`, `tokenExpired` or `tokenInvalid`.

{% tabs %}
{% tab title="Redacted response" %}

```json
{
  "hash": "0xa7696d596aa08173305084541a219935ffe3a87728ae32ba8efc7db096b4636f",
  "chainId": "1",
  "type": "swap",
  "status": "completed",
  "fromAsset": "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
  "fromAmount": "30",
  "toAsset": "",
  "toAmount": "",
  "toAddress": "",
  "legs": [
    {
      "hash": "0xa7696d596aa08173305084541a219935ffe3a87728ae32ba8efc7db096b4636f",
      "chainId": "1",
      "type": "token_transfer",
      "status": "completed",
      "toAddress": "0xaBEC67AEf41c426a30c54de790D9efFb2D561962"
    },
    {
      "hash": "",
      "chainId": "1",
      "type": "swap",
      "status": "completed",
      "fromAmount": "",
      "toAsset": "",
      "toAmount": "",
      "toAddress": "",
      "block": -1,
      "finalisedAt": -1
    }
  ],
  "meta": {
    "privacyMode": true,
    "redacted": true,
    "redactionReason": "notOwner"
  }
}
```

Trimmed to the fields that matter here.
{% endtab %}
{% endtabs %}

**Your API key unlocks the rest.** Call `/track` with the same `x-api-key` you used on `/v3/swap`:

{% tabs %}
{% tab title="cURL" %}

```sh
curl -X 'POST' \
  'https://api.swapkit.dev/track' \
  -H 'Content-Type: application/json' \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "hash": "0xa7696d596aa08173305084541a219935ffe3a87728ae32ba8efc7db096b4636f",
    "chainId": "1"
}'
```

{% endtab %}
{% endtabs %}

You get the full swap back, plus a root `meta.explorerUrl` — a SwapKit Explorer link carrying an **access token** in its `t` parameter. See [Explorer](#explorer).

{% tabs %}
{% tab title="Full response" %}

```json
{
  "hash": "0xa7696d596aa08173305084541a219935ffe3a87728ae32ba8efc7db096b4636f",
  "chainId": "1",
  "type": "swap",
  "status": "completed",
  "fromAsset": "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
  "fromAmount": "30",
  "toAsset": "BTC.BTC(OMNI)-coin",
  "toAmount": "0.00033726",
  "toAddress": "bc1qpcnx3d88qtl0aqz8pwsrjpwxr6j5q4ly8kzezx",
  "legs": [
    { "chainId": "1", "type": "token_transfer", "status": "completed" },
    { "chainId": "near", "type": "swap", "status": "completed" },
    { "chainId": "bitcoin", "type": "token_transfer", "status": "completed" }
  ],
  "meta": {
    "provider": "NEAR",
    "privacyMode": true,
    "explorerUrl": "https://track.swapkit.dev/?hash=0xa7696d596aa08173305084541a219935ffe3a87728ae32ba8efc7db096b4636f&chainId=1&t=YOUR_ACCESS_TOKEN"
  }
}
```

Trimmed to the fields that matter here. There's no `meta.redacted` on a full view.
{% endtab %}
{% endtabs %}

{% hint style="warning" %}
**Only the key that executed the swap works.** Any other key gets the redacted view, with `meta.redactionReason: "notOwner"` and no access token.
{% endhint %}

The full list of redacted fields, and how to pass a token to `/track` directly, are under [Confidential swaps](/api-reference/track-request-the-status-of-a-swap.md#confidential-swaps) on the `/track` page.

### Explorer

Open `meta.explorerUrl` in a browser and the [SwapKit Explorer](https://track.swapkit.dev/) shows the whole swap — every leg, the destination and the amount received. The token in the link does the authorising, so no API key is needed: hand it to your user, your support team, or anyone else who needs to follow the swap.

The Explorer renders each redacted field as a row of asterisks. Over the API those same fields come back as empty strings — same redaction, different presentation.

{% columns %}
{% column %}

<figure><img src="https://1967725871-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl88kbsjWGhgdARxzgqCE%2Fuploads%2Fgit-blob-d7c9c81971607587ac2a3e60f106d491368197fd%2Fexplorer_redacted.png?alt=media" alt="SwapKit Explorer showing a confidential swap without an access token: the provider reads N/A, the destination address and timestamp render as rows of asterisks, and the raw API response below shows the same fields redacted"><figcaption><p>Without a token — the deposit only</p></figcaption></figure>
{% endcolumn %}

{% column %}

<figure><img src="https://1967725871-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl88kbsjWGhgdARxzgqCE%2Fuploads%2Fgit-blob-843fcc140d400ea7294b417b3d10e5eed846ee08%2Fexplorer_unlocked.png?alt=media" alt="The same swap opened with a t= access token: the provider reads NEAR, the destination address, amount and timestamp are shown, and the raw API response below shows every leg in full"><figcaption><p>With <code>t=</code> — the whole swap</p></figcaption></figure>
{% endcolumn %}
{% endcolumns %}

The link has the form `https://track.swapkit.dev/?hash=<hash>&chainId=<chainId>&t=<token>`. The token is tied to that one swap, so it can't unlock any other.

{% hint style="warning" %}
**Fetch the link after the deposit lands.** The link and its token are built from the deposit's transaction hash. Track by `depositChannelId` or `depositAddress` before the deposit is seen and the swap has no hash yet, so `explorerUrl` won't open it. Once the response has a `hash`, every lookup returns a working link.
{% endhint %}

**Links expire 24 hours after they're issued.** After that, the Explorer falls back to public mode and shows the redacted swap — the same view as anyone without a token, with `meta.redactionReason: "tokenExpired"`.

**To get a fresh link, call `/track` with your API key again.** Every call made with your key mints a new token. Opening or sharing a link doesn't extend it, so fetch `explorerUrl` when you need to show it rather than storing it.

### Questions

Talk to your SwapKit contact if you need privacy on a pair that returns no private route, or want to confirm whether your key's requests are reaching the privacy-capable providers.
